2
0

smacks.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. (function (root, factory) {
  2. define(["jasmine", "mock", "test-utils"], factory);
  3. } (this, function (jasmine, mock, test_utils) {
  4. "use strict";
  5. const $iq = converse.env.$iq;
  6. const $msg = converse.env.$msg;
  7. const Strophe = converse.env.Strophe;
  8. const sizzle = converse.env.sizzle;
  9. const u = converse.env.utils;
  10. describe("XEP-0198 Stream Management", function () {
  11. it("gets enabled with an <enable> stanza and resumed with a <resume> stanza",
  12. mock.initConverse(
  13. ['chatBoxesInitialized'],
  14. { 'auto_login': false,
  15. 'enable_smacks': true,
  16. 'show_controlbox_by_default': true,
  17. 'smacks_max_unacked_stanzas': 2
  18. },
  19. async function (done, _converse) {
  20. const view = _converse.chatboxviews.get('controlbox');
  21. spyOn(view, 'renderControlBoxPane').and.callThrough();
  22. _converse.api.user.login('romeo@montague.lit/orchard', 'secret');
  23. const sent_stanzas = _converse.connection.sent_stanzas;
  24. let stanza = await u.waitUntil(() =>
  25. sent_stanzas.filter(s => (s.tagName === 'enable')).pop());
  26. expect(_converse.session.get('smacks_enabled')).toBe(false);
  27. expect(Strophe.serialize(stanza)).toEqual('<enable resume="true" xmlns="urn:xmpp:sm:3"/>');
  28. let result = u.toStanza(`<enabled xmlns="urn:xmpp:sm:3" id="some-long-sm-id" resume="true"/>`);
  29. _converse.connection._dataRecv(test_utils.createRequest(result));
  30. expect(_converse.session.get('smacks_enabled')).toBe(true);
  31. await u.waitUntil(() => view.renderControlBoxPane.calls.count());
  32. let IQ_stanzas = _converse.connection.IQ_stanzas;
  33. await u.waitUntil(() => IQ_stanzas.length === 4);
  34. let iq = IQ_stanzas[IQ_stanzas.length-1];
  35. expect(Strophe.serialize(iq)).toBe(
  36. `<iq id="${iq.getAttribute('id')}" type="get" xmlns="jabber:client"><query xmlns="jabber:iq:roster"/></iq>`);
  37. await test_utils.waitForRoster(_converse, 'current', 1);
  38. IQ_stanzas.pop();
  39. const disco_iq = IQ_stanzas.pop();
  40. expect(Strophe.serialize(disco_iq)).toBe(
  41. `<iq from="romeo@montague.lit" id="${disco_iq.getAttribute('id')}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+
  42. `<pubsub xmlns="http://jabber.org/protocol/pubsub"><items node="eu.siacs.conversations.axolotl.devicelist"/></pubsub></iq>`);
  43. iq = IQ_stanzas.pop();
  44. expect(Strophe.serialize(iq)).toBe(
  45. `<iq from="romeo@montague.lit/orchard" id="${iq.getAttribute('id')}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+
  46. `<query xmlns="http://jabber.org/protocol/disco#info"/></iq>`);
  47. iq = IQ_stanzas.pop();
  48. expect(Strophe.serialize(iq)).toBe(
  49. `<iq from="romeo@montague.lit/orchard" id="${iq.getAttribute('id')}" to="montague.lit" type="get" xmlns="jabber:client">`+
  50. `<query xmlns="http://jabber.org/protocol/disco#info"/></iq>`);
  51. expect(sent_stanzas.filter(s => (s.nodeName === 'r')).length).toBe(2);
  52. expect(_converse.session.get('unacked_stanzas').length).toBe(5);
  53. // test handling of acks
  54. let ack = u.toStanza(`<a xmlns="urn:xmpp:sm:3" h="2"/>`);
  55. _converse.connection._dataRecv(test_utils.createRequest(ack));
  56. expect(_converse.session.get('unacked_stanzas').length).toBe(3);
  57. // test handling of ack requests
  58. let r = u.toStanza(`<r xmlns="urn:xmpp:sm:3"/>`);
  59. _converse.connection._dataRecv(test_utils.createRequest(r));
  60. ack = await u.waitUntil(() => sent_stanzas.filter(s => (s.nodeName === 'a')).pop());
  61. expect(Strophe.serialize(ack)).toBe('<a h="1" xmlns="urn:xmpp:sm:3"/>');
  62. const disco_result = $iq({
  63. 'type': 'result',
  64. 'from': 'montague.lit',
  65. 'to': 'romeo@montague.lit/orchard',
  66. 'id': disco_iq.getAttribute('id'),
  67. }).c('query', {'xmlns': 'http://jabber.org/protocol/disco#info'})
  68. .c('identity', {
  69. 'category': 'server',
  70. 'type': 'im'
  71. }).up()
  72. .c('feature', {'var': 'http://jabber.org/protocol/disco#info'}).up()
  73. .c('feature', {'var': 'http://jabber.org/protocol/disco#items'});
  74. _converse.connection._dataRecv(test_utils.createRequest(disco_result));
  75. ack = u.toStanza(`<a xmlns="urn:xmpp:sm:3" h="3"/>`);
  76. _converse.connection._dataRecv(test_utils.createRequest(ack));
  77. expect(_converse.session.get('unacked_stanzas').length).toBe(2);
  78. r = u.toStanza(`<r xmlns="urn:xmpp:sm:3"/>`);
  79. _converse.connection._dataRecv(test_utils.createRequest(r));
  80. ack = await u.waitUntil(() => sent_stanzas.filter(s => (s.nodeName === 'a' && s.getAttribute('h') === '1')).pop());
  81. expect(Strophe.serialize(ack)).toBe('<a h="1" xmlns="urn:xmpp:sm:3"/>');
  82. // test session resumption
  83. _converse.connection.IQ_stanzas = [];
  84. IQ_stanzas = _converse.connection.IQ_stanzas;
  85. await _converse.api.connection.reconnect();
  86. stanza = await u.waitUntil(() => sent_stanzas.filter(s => (s.tagName === 'resume')).pop());
  87. expect(Strophe.serialize(stanza)).toEqual('<resume h="2" previd="some-long-sm-id" xmlns="urn:xmpp:sm:3"/>');
  88. result = u.toStanza(`<resumed xmlns="urn:xmpp:sm:3" h="another-sequence-number" previd="some-long-sm-id"/>`);
  89. _converse.connection._dataRecv(test_utils.createRequest(result));
  90. // Another <enable> stanza doesn't get sent out
  91. expect(sent_stanzas.filter(s => (s.tagName === 'enable')).length).toBe(1);
  92. expect(_converse.session.get('smacks_enabled')).toBe(true);
  93. await u.waitUntil(() => IQ_stanzas.length === 1);
  94. // Test that unacked stanzas get resent out
  95. iq = IQ_stanzas.pop();
  96. expect(Strophe.serialize(iq)).toBe(`<iq id="${iq.getAttribute('id')}" type="get" xmlns="jabber:client"><query xmlns="jabber:iq:roster"/></iq>`);
  97. expect(IQ_stanzas.filter(iq => sizzle('query[xmlns="jabber:iq:roster"]', iq).pop()).length).toBe(0);
  98. await _converse.api.waitUntil('statusInitialized');
  99. done();
  100. }));
  101. it("might not resume and the session will then be reset",
  102. mock.initConverse(
  103. ['chatBoxesInitialized'],
  104. { 'auto_login': false,
  105. 'enable_smacks': true,
  106. 'show_controlbox_by_default': true,
  107. 'smacks_max_unacked_stanzas': 2
  108. },
  109. async function (done, _converse) {
  110. _converse.api.user.login('romeo@montague.lit/orchard', 'secret');
  111. const sent_stanzas = _converse.connection.sent_stanzas;
  112. let stanza = await u.waitUntil(() => sent_stanzas.filter(s => (s.tagName === 'enable')).pop());
  113. expect(Strophe.serialize(stanza)).toEqual('<enable resume="true" xmlns="urn:xmpp:sm:3"/>');
  114. let result = u.toStanza(`<enabled xmlns="urn:xmpp:sm:3" id="some-long-sm-id" resume="true"/>`);
  115. _converse.connection._dataRecv(test_utils.createRequest(result));
  116. await test_utils.waitForRoster(_converse, 'current', 1);
  117. // test session resumption
  118. await _converse.api.connection.reconnect();
  119. stanza = await u.waitUntil(() => sent_stanzas.filter(s => (s.tagName === 'resume')).pop());
  120. expect(Strophe.serialize(stanza)).toEqual('<resume h="1" previd="some-long-sm-id" xmlns="urn:xmpp:sm:3"/>');
  121. result = u.toStanza(
  122. `<failed xmlns="urn:xmpp:sm:3" h="another-sequence-number">`+
  123. `<item-not-found xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>`+
  124. `</failed>`);
  125. _converse.connection._dataRecv(test_utils.createRequest(result));
  126. // Session data gets reset
  127. expect(_converse.session.get('smacks_enabled')).toBe(false);
  128. expect(_converse.session.get('num_stanzas_handled')).toBe(0);
  129. expect(_converse.session.get('num_stanzas_handled_by_server')).toBe(0);
  130. expect(_converse.session.get('num_stanzas_since_last_ack')).toBe(0);
  131. expect(_converse.session.get('unacked_stanzas').length).toBe(0);
  132. expect(_converse.session.get('roster_cached')).toBeFalsy();
  133. await u.waitUntil(() => sent_stanzas.filter(s => (s.tagName === 'enable')).length === 2);
  134. stanza = sent_stanzas.filter(s => (s.tagName === 'enable')).pop();
  135. expect(Strophe.serialize(stanza)).toEqual('<enable resume="true" xmlns="urn:xmpp:sm:3"/>');
  136. result = u.toStanza(`<enabled xmlns="urn:xmpp:sm:3" id="another-long-sm-id" resume="true"/>`);
  137. _converse.connection._dataRecv(test_utils.createRequest(result));
  138. expect(_converse.session.get('smacks_enabled')).toBe(true);
  139. // Check that the roster gets fetched
  140. await test_utils.waitForRoster(_converse, 'current', 1);
  141. done();
  142. }));
  143. it("will handle MUC messages sent during disconnection",
  144. mock.initConverse(
  145. ['chatBoxesInitialized'],
  146. { 'auto_login': false,
  147. 'enable_smacks': true,
  148. 'show_controlbox_by_default': true,
  149. 'blacklisted_plugins': 'converse-mam',
  150. 'smacks_max_unacked_stanzas': 2
  151. },
  152. async function (done, _converse) {
  153. const key = "converse-test-session/converse.session-romeo@montague.lit-converse.session-romeo@montague.lit";
  154. sessionStorage.setItem(
  155. key,
  156. JSON.stringify({
  157. "id": "converse.session-romeo@montague.lit",
  158. "jid": "romeo@montague.lit/converse.js-100020907",
  159. "bare_jid": "romeo@montague.lit",
  160. "resource": "converse.js-100020907",
  161. "domain": "montague.lit",
  162. "active": false,
  163. "smacks_enabled": true,
  164. "num_stanzas_handled": 580,
  165. "num_stanzas_handled_by_server": 525,
  166. "num_stanzas_since_last_ack": 0,
  167. "unacked_stanzas": [],
  168. "smacks_stream_id": "some-long-sm-id",
  169. "push_enabled": ["romeo@montague.lit"],
  170. "carbons_enabled": true,
  171. "roster_cached": true
  172. })
  173. )
  174. _converse.api.user.login('romeo@montague.lit', 'secret');
  175. const sent_stanzas = _converse.connection.sent_stanzas;
  176. const stanza = await u.waitUntil(() => sent_stanzas.filter(s => (s.tagName === 'resume')).pop());
  177. expect(Strophe.serialize(stanza)).toEqual('<resume h="580" previd="some-long-sm-id" xmlns="urn:xmpp:sm:3"/>');
  178. const result = u.toStanza(`<resumed xmlns="urn:xmpp:sm:3" h="another-sequence-number" previd="some-long-sm-id"/>`);
  179. _converse.connection._dataRecv(test_utils.createRequest(result));
  180. expect(_converse.session.get('smacks_enabled')).toBe(true);
  181. const muc_jid = 'lounge@montague.lit/some1';
  182. // A MUC message gets received
  183. const msg = $msg({
  184. from: muc_jid,
  185. id: u.getUniqueId(),
  186. to: 'romeo@montague.lit',
  187. type: 'groupchat'
  188. }).c('body').t('First message').tree();
  189. _converse.connection._dataRecv(test_utils.createRequest(msg));
  190. expect(_converse.session.get('smacks_received_stanzas').length).toBe(1);
  191. await _converse.api.waitUntil('statusInitialized');
  192. // Test now that when a MUC gets opened, it checks whether there
  193. // are SMACKS messages waiting for it.
  194. await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
  195. await u.waitUntil(() => view.el.querySelectorAll('.chat-msg').length === 1);
  196. done();
  197. }));
  198. });
  199. }));