websocket_message.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621
  1. package httpexpect
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "github.com/gorilla/websocket"
  7. )
  8. // WebsocketMessage provides methods to inspect message read from WebSocket connection.
  9. type WebsocketMessage struct {
  10. noCopy noCopy
  11. chain *chain
  12. typ int
  13. content []byte
  14. closeCode int
  15. }
  16. // NewWebsocketMessage returns a new WebsocketMessage instance.
  17. //
  18. // If reporter is nil, the function panics.
  19. // Content may be nil.
  20. //
  21. // Example:
  22. //
  23. // m := NewWebsocketMessage(t, websocket.TextMessage, []byte("content"), 0)
  24. // m.TextMessage()
  25. func NewWebsocketMessage(
  26. reporter Reporter, typ int, content []byte, closeCode ...int,
  27. ) *WebsocketMessage {
  28. return newWebsocketMessage(
  29. newChainWithDefaults("WebsocketMessage()", reporter),
  30. typ,
  31. content,
  32. closeCode...,
  33. )
  34. }
  35. // NewWebsocketMessageC returns a new WebsocketMessage instance with config.
  36. //
  37. // Requirements for config are same as for WithConfig function.
  38. // Content may be nil.
  39. //
  40. // Example:
  41. //
  42. // m := NewWebsocketMessageC(config, websocket.TextMessage, []byte("content"), 0)
  43. // m.TextMessage()
  44. func NewWebsocketMessageC(
  45. config Config, typ int, content []byte, closeCode ...int,
  46. ) *WebsocketMessage {
  47. return newWebsocketMessage(
  48. newChainWithConfig("WebsocketMessage()", config.withDefaults()),
  49. typ,
  50. content,
  51. closeCode...,
  52. )
  53. }
  54. func newWebsocketMessage(
  55. parent *chain, typ int, content []byte, closeCode ...int,
  56. ) *WebsocketMessage {
  57. wm := newEmptyWebsocketMessage(parent)
  58. opChain := wm.chain.enter("")
  59. defer opChain.leave()
  60. wm.typ = typ
  61. wm.content = content
  62. if len(closeCode) > 1 {
  63. opChain.fail(AssertionFailure{
  64. Type: AssertUsage,
  65. Errors: []error{
  66. errors.New("unexpected multiple closeCode arguments"),
  67. },
  68. })
  69. return wm
  70. }
  71. if len(closeCode) != 0 {
  72. wm.closeCode = closeCode[0]
  73. }
  74. return wm
  75. }
  76. func newEmptyWebsocketMessage(parent *chain) *WebsocketMessage {
  77. return &WebsocketMessage{
  78. chain: parent.clone(),
  79. }
  80. }
  81. // Raw returns underlying type, content and close code of WebSocket message.
  82. // Theses values are originally read from WebSocket connection.
  83. func (wm *WebsocketMessage) Raw() (typ int, content []byte, closeCode int) {
  84. return wm.typ, wm.content, wm.closeCode
  85. }
  86. // Alias is similar to Value.Alias.
  87. func (wm *WebsocketMessage) Alias(name string) *WebsocketMessage {
  88. opChain := wm.chain.enter("Alias(%q)", name)
  89. defer opChain.leave()
  90. wm.chain.setAlias(name)
  91. return wm
  92. }
  93. // CloseMessage is a shorthand for m.Type(websocket.CloseMessage).
  94. func (wm *WebsocketMessage) CloseMessage() *WebsocketMessage {
  95. opChain := wm.chain.enter("CloseMessage()")
  96. defer opChain.leave()
  97. wm.checkType(opChain, websocket.CloseMessage)
  98. return wm
  99. }
  100. // NotCloseMessage is a shorthand for m.NotType(websocket.CloseMessage).
  101. func (wm *WebsocketMessage) NotCloseMessage() *WebsocketMessage {
  102. opChain := wm.chain.enter("NotCloseMessage()")
  103. defer opChain.leave()
  104. wm.checkNotType(opChain, websocket.CloseMessage)
  105. return wm
  106. }
  107. // BinaryMessage is a shorthand for m.Type(websocket.BinaryMessage).
  108. func (wm *WebsocketMessage) BinaryMessage() *WebsocketMessage {
  109. opChain := wm.chain.enter("BinaryMessage()")
  110. defer opChain.leave()
  111. wm.checkType(opChain, websocket.BinaryMessage)
  112. return wm
  113. }
  114. // NotBinaryMessage is a shorthand for m.NotType(websocket.BinaryMessage).
  115. func (wm *WebsocketMessage) NotBinaryMessage() *WebsocketMessage {
  116. opChain := wm.chain.enter("NotBinaryMessage()")
  117. defer opChain.leave()
  118. wm.checkNotType(opChain, websocket.BinaryMessage)
  119. return wm
  120. }
  121. // TextMessage is a shorthand for m.Type(websocket.TextMessage).
  122. func (wm *WebsocketMessage) TextMessage() *WebsocketMessage {
  123. opChain := wm.chain.enter("TextMessage()")
  124. defer opChain.leave()
  125. wm.checkType(opChain, websocket.TextMessage)
  126. return wm
  127. }
  128. // NotTextMessage is a shorthand for m.NotType(websocket.TextMessage).
  129. func (wm *WebsocketMessage) NotTextMessage() *WebsocketMessage {
  130. opChain := wm.chain.enter("NotTextMessage()")
  131. defer opChain.leave()
  132. wm.checkNotType(opChain, websocket.TextMessage)
  133. return wm
  134. }
  135. // Type succeeds if WebSocket message type is one of the given.
  136. //
  137. // WebSocket message types are defined in RFC 6455, section 11.8.
  138. // See also https://godoc.org/github.com/gorilla/websocket#pkg-constants
  139. //
  140. // Example:
  141. //
  142. // msg := conn.Expect()
  143. // msg.Type(websocket.TextMessage, websocket.BinaryMessage)
  144. func (wm *WebsocketMessage) Type(types ...int) *WebsocketMessage {
  145. opChain := wm.chain.enter("Type()")
  146. defer opChain.leave()
  147. wm.checkType(opChain, types...)
  148. return wm
  149. }
  150. // NotType succeeds if WebSocket message type is none of the given.
  151. //
  152. // WebSocket message types are defined in RFC 6455, section 11.8.
  153. // See also https://godoc.org/github.com/gorilla/websocket#pkg-constants
  154. //
  155. // Example:
  156. //
  157. // msg := conn.Expect()
  158. // msg.NotType(websocket.CloseMessage, websocket.BinaryMessage)
  159. func (wm *WebsocketMessage) NotType(types ...int) *WebsocketMessage {
  160. opChain := wm.chain.enter("NotType()")
  161. defer opChain.leave()
  162. wm.checkNotType(opChain, types...)
  163. return wm
  164. }
  165. func (wm *WebsocketMessage) checkType(opChain *chain, types ...int) {
  166. if opChain.failed() {
  167. return
  168. }
  169. if len(types) == 0 {
  170. opChain.fail(AssertionFailure{
  171. Type: AssertUsage,
  172. Errors: []error{
  173. errors.New("missing type argument"),
  174. },
  175. })
  176. return
  177. }
  178. found := false
  179. for _, t := range types {
  180. if t == wm.typ {
  181. found = true
  182. break
  183. }
  184. }
  185. if !found {
  186. if len(types) == 1 {
  187. opChain.fail(AssertionFailure{
  188. Type: AssertEqual,
  189. Actual: &AssertionValue{wsMessageType(wm.typ)},
  190. Expected: &AssertionValue{wsMessageType(types[0])},
  191. Errors: []error{
  192. errors.New("expected: message types are equal"),
  193. },
  194. })
  195. } else {
  196. typeList := make([]interface{}, 0, len(types))
  197. for _, t := range types {
  198. typeList = append(typeList, wsMessageType(t))
  199. }
  200. opChain.fail(AssertionFailure{
  201. Type: AssertBelongs,
  202. Actual: &AssertionValue{wsMessageType(wm.typ)},
  203. Expected: &AssertionValue{AssertionList(typeList)},
  204. Errors: []error{
  205. errors.New("expected: message type belongs to given list"),
  206. },
  207. })
  208. }
  209. }
  210. }
  211. func (wm *WebsocketMessage) checkNotType(opChain *chain, types ...int) {
  212. if opChain.failed() {
  213. return
  214. }
  215. if len(types) == 0 {
  216. opChain.fail(AssertionFailure{
  217. Type: AssertUsage,
  218. Errors: []error{
  219. errors.New("missing type argument"),
  220. },
  221. })
  222. return
  223. }
  224. found := false
  225. for _, t := range types {
  226. if t == wm.typ {
  227. found = true
  228. break
  229. }
  230. }
  231. if found {
  232. if len(types) == 1 {
  233. opChain.fail(AssertionFailure{
  234. Type: AssertNotEqual,
  235. Actual: &AssertionValue{wsMessageType(wm.typ)},
  236. Expected: &AssertionValue{wsMessageType(types[0])},
  237. Errors: []error{
  238. errors.New("expected: message types are non-equal"),
  239. },
  240. })
  241. } else {
  242. typeList := make([]interface{}, 0, len(types))
  243. for _, t := range types {
  244. typeList = append(typeList, wsMessageType(t))
  245. }
  246. opChain.fail(AssertionFailure{
  247. Type: AssertNotBelongs,
  248. Actual: &AssertionValue{wsMessageType(wm.typ)},
  249. Expected: &AssertionValue{AssertionList(typeList)},
  250. Errors: []error{
  251. errors.New("expected: message type does not belong to given list"),
  252. },
  253. })
  254. }
  255. }
  256. }
  257. // Code succeeds if WebSocket close code is one of the given.
  258. //
  259. // Code fails if WebSocket message type is not "8 - Connection Close Frame".
  260. //
  261. // WebSocket close codes are defined in RFC 6455, section 11.7.
  262. // See also https://godoc.org/github.com/gorilla/websocket#pkg-constants
  263. //
  264. // Example:
  265. //
  266. // msg := conn.Expect().Closed()
  267. // msg.Code(websocket.CloseNormalClosure, websocket.CloseGoingAway)
  268. func (wm *WebsocketMessage) Code(codes ...int) *WebsocketMessage {
  269. opChain := wm.chain.enter("Code()")
  270. defer opChain.leave()
  271. wm.checkCode(opChain, codes...)
  272. return wm
  273. }
  274. // NotCode succeeds if WebSocket close code is none of the given.
  275. //
  276. // NotCode fails if WebSocket message type is not "8 - Connection Close Frame".
  277. //
  278. // WebSocket close codes are defined in RFC 6455, section 11.7.
  279. // See also https://godoc.org/github.com/gorilla/websocket#pkg-constants
  280. //
  281. // Example:
  282. //
  283. // msg := conn.Expect().Closed()
  284. // msg.NotCode(websocket.CloseAbnormalClosure, websocket.CloseNoStatusReceived)
  285. func (wm *WebsocketMessage) NotCode(codes ...int) *WebsocketMessage {
  286. opChain := wm.chain.enter("NotCode()")
  287. defer opChain.leave()
  288. wm.checkNotCode(opChain, codes...)
  289. return wm
  290. }
  291. func (wm *WebsocketMessage) checkCode(opChain *chain, codes ...int) {
  292. if opChain.failed() {
  293. return
  294. }
  295. if len(codes) == 0 {
  296. opChain.fail(AssertionFailure{
  297. Type: AssertUsage,
  298. Errors: []error{
  299. errors.New("missing code argument"),
  300. },
  301. })
  302. return
  303. }
  304. if wm.typ != websocket.CloseMessage {
  305. opChain.fail(AssertionFailure{
  306. Type: AssertEqual,
  307. Actual: &AssertionValue{wsMessageType(wm.typ)},
  308. Expected: &AssertionValue{wsMessageType(websocket.CloseMessage)},
  309. Errors: []error{
  310. errors.New("expected: close message"),
  311. },
  312. })
  313. return
  314. }
  315. found := false
  316. for _, c := range codes {
  317. if c == wm.closeCode {
  318. found = true
  319. break
  320. }
  321. }
  322. if !found {
  323. if len(codes) == 1 {
  324. opChain.fail(AssertionFailure{
  325. Type: AssertEqual,
  326. Actual: &AssertionValue{wsCloseCode(wm.closeCode)},
  327. Expected: &AssertionValue{wsCloseCode(codes[0])},
  328. Errors: []error{
  329. errors.New("expected: close codes are equal"),
  330. },
  331. })
  332. } else {
  333. codeList := make([]interface{}, 0, len(codes))
  334. for _, c := range codes {
  335. codeList = append(codeList, wsCloseCode(c))
  336. }
  337. opChain.fail(AssertionFailure{
  338. Type: AssertBelongs,
  339. Actual: &AssertionValue{wsCloseCode(wm.closeCode)},
  340. Expected: &AssertionValue{AssertionList(codeList)},
  341. Errors: []error{
  342. errors.New("expected: close code belongs to given list"),
  343. },
  344. })
  345. }
  346. }
  347. }
  348. func (wm *WebsocketMessage) checkNotCode(opChain *chain, codes ...int) {
  349. if opChain.failed() {
  350. return
  351. }
  352. if len(codes) == 0 {
  353. opChain.fail(AssertionFailure{
  354. Type: AssertUsage,
  355. Errors: []error{
  356. errors.New("missing code argument"),
  357. },
  358. })
  359. return
  360. }
  361. if wm.typ != websocket.CloseMessage {
  362. opChain.fail(AssertionFailure{
  363. Type: AssertEqual,
  364. Actual: &AssertionValue{wsMessageType(wm.typ)},
  365. Expected: &AssertionValue{wsMessageType(websocket.CloseMessage)},
  366. Errors: []error{
  367. errors.New("expected: close message"),
  368. },
  369. })
  370. return
  371. }
  372. found := false
  373. for _, c := range codes {
  374. if c == wm.closeCode {
  375. found = true
  376. break
  377. }
  378. }
  379. if found {
  380. if len(codes) == 1 {
  381. opChain.fail(AssertionFailure{
  382. Type: AssertNotEqual,
  383. Actual: &AssertionValue{wsCloseCode(wm.closeCode)},
  384. Expected: &AssertionValue{wsCloseCode(codes[0])},
  385. Errors: []error{
  386. errors.New("expected: close codes are non-equal"),
  387. },
  388. })
  389. } else {
  390. codeList := make([]interface{}, 0, len(codes))
  391. for _, c := range codes {
  392. codeList = append(codeList, wsCloseCode(c))
  393. }
  394. opChain.fail(AssertionFailure{
  395. Type: AssertNotBelongs,
  396. Actual: &AssertionValue{wsCloseCode(wm.closeCode)},
  397. Expected: &AssertionValue{AssertionList(codeList)},
  398. Errors: []error{
  399. errors.New("expected: close code dose not belong to given list"),
  400. },
  401. })
  402. }
  403. }
  404. }
  405. // NoContent succeeds if WebSocket message has no content (is empty).
  406. func (wm *WebsocketMessage) NoContent() *WebsocketMessage {
  407. opChain := wm.chain.enter("NoContent()")
  408. defer opChain.leave()
  409. if opChain.failed() {
  410. return wm
  411. }
  412. if !(len(wm.content) == 0) {
  413. var actual interface{}
  414. switch wm.typ {
  415. case websocket.BinaryMessage:
  416. actual = wm.content
  417. default:
  418. actual = string(wm.content)
  419. }
  420. opChain.fail(AssertionFailure{
  421. Type: AssertEmpty,
  422. Actual: &AssertionValue{actual},
  423. Errors: []error{
  424. errors.New("expected: message content is empty"),
  425. },
  426. })
  427. }
  428. return wm
  429. }
  430. // Body returns a new String instance with WebSocket message content.
  431. //
  432. // Example:
  433. //
  434. // msg := conn.Expect()
  435. // msg.Body().NotEmpty()
  436. // msg.Body().Length().IsEqual(100)
  437. func (wm *WebsocketMessage) Body() *String {
  438. opChain := wm.chain.enter("Body()")
  439. defer opChain.leave()
  440. if opChain.failed() {
  441. return newString(opChain, "")
  442. }
  443. return newString(opChain, string(wm.content))
  444. }
  445. // JSON returns a new Value instance with JSON contents of WebSocket message.
  446. //
  447. // JSON succeeds if JSON may be decoded from message content.
  448. //
  449. // Example:
  450. //
  451. // msg := conn.Expect()
  452. // msg.JSON().Array().ConsistsOf("foo", "bar")
  453. func (wm *WebsocketMessage) JSON() *Value {
  454. opChain := wm.chain.enter("JSON()")
  455. defer opChain.leave()
  456. if opChain.failed() {
  457. return newValue(opChain, nil)
  458. }
  459. var value interface{}
  460. if err := json.Unmarshal(wm.content, &value); err != nil {
  461. opChain.fail(AssertionFailure{
  462. Type: AssertValid,
  463. Actual: &AssertionValue{
  464. string(wm.content),
  465. },
  466. Errors: []error{
  467. errors.New("failed to decode json"),
  468. err,
  469. },
  470. })
  471. return newValue(opChain, nil)
  472. }
  473. return newValue(opChain, value)
  474. }
  475. type wsMessageType int
  476. func (wmt wsMessageType) String() string {
  477. s := "unknown"
  478. switch wmt {
  479. case websocket.TextMessage:
  480. s = "text"
  481. case websocket.BinaryMessage:
  482. s = "binary"
  483. case websocket.CloseMessage:
  484. s = "close"
  485. case websocket.PingMessage:
  486. s = "ping"
  487. case websocket.PongMessage:
  488. s = "pong"
  489. }
  490. return fmt.Sprintf("%s(%d)", s, wmt)
  491. }
  492. type wsCloseCode int
  493. // https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/code
  494. func (wcc wsCloseCode) String() string {
  495. s := "Unknown"
  496. switch wcc {
  497. case 1000:
  498. s = "NormalClosure"
  499. case 1001:
  500. s = "GoingAway"
  501. case 1002:
  502. s = "ProtocolError"
  503. case 1003:
  504. s = "UnsupportedData"
  505. case 1004:
  506. s = "Reserved"
  507. case 1005:
  508. s = "NoStatusReceived"
  509. case 1006:
  510. s = "AbnormalClosure"
  511. case 1007:
  512. s = "InvalidFramePayloadData"
  513. case 1008:
  514. s = "PolicyViolation"
  515. case 1009:
  516. s = "MessageTooBig"
  517. case 1010:
  518. s = "MandatoryExtension"
  519. case 1011:
  520. s = "InternalServerError"
  521. case 1012:
  522. s = "ServiceRestart"
  523. case 1013:
  524. s = "TryAgainLater"
  525. case 1014:
  526. s = "BadGateway"
  527. case 1015:
  528. s = "TLSHandshake"
  529. }
  530. return fmt.Sprintf("%s(%d)", s, wcc)
  531. }