smacks.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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 Strophe = converse.env.Strophe;
  7. const sizzle = converse.env.sizzle;
  8. const u = converse.env.utils;
  9. describe("XEP-0198 Stream Management", function () {
  10. it("gets enabled with an <enable> stanza and resumed with a <resume> stanza",
  11. mock.initConverse(
  12. ['chatBoxesInitialized'],
  13. { 'auto_login': false,
  14. 'enable_smacks': true,
  15. 'show_controlbox_by_default': true,
  16. 'smacks_max_unacked_stanzas': 2
  17. },
  18. async function (done, _converse) {
  19. const view = _converse.chatboxviews.get('controlbox');
  20. spyOn(view, 'renderControlBoxPane').and.callThrough();
  21. _converse.api.user.login('romeo@montague.lit/orchard', 'secret');
  22. const sent_stanzas = _converse.connection.sent_stanzas;
  23. let stanza = await u.waitUntil(() =>
  24. sent_stanzas.filter(s => (s.tagName === 'enable')).pop());
  25. expect(_converse.session.get('smacks_enabled')).toBe(false);
  26. expect(Strophe.serialize(stanza)).toEqual('<enable resume="true" xmlns="urn:xmpp:sm:3"/>');
  27. let result = u.toStanza(`<enabled xmlns="urn:xmpp:sm:3" id="some-long-sm-id" resume="true"/>`);
  28. _converse.connection._dataRecv(test_utils.createRequest(result));
  29. expect(_converse.session.get('smacks_enabled')).toBe(true);
  30. await u.waitUntil(() => view.renderControlBoxPane.calls.count());
  31. let IQ_stanzas = _converse.connection.IQ_stanzas;
  32. await u.waitUntil(() => IQ_stanzas.length === 4);
  33. let iq = IQ_stanzas.pop();
  34. expect(Strophe.serialize(iq)).toBe(
  35. `<iq from="romeo@montague.lit/orchard" id="${iq.getAttribute('id')}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+
  36. `<query xmlns="http://jabber.org/protocol/disco#info"/></iq>`);
  37. iq = IQ_stanzas[IQ_stanzas.length-1];
  38. expect(Strophe.serialize(iq)).toBe(
  39. `<iq id="${iq.getAttribute('id')}" type="get" xmlns="jabber:client"><query xmlns="jabber:iq:roster"/></iq>`);
  40. await test_utils.waitForRoster(_converse, 'current', 1);
  41. IQ_stanzas.pop();
  42. const disco_iq = IQ_stanzas.pop();
  43. expect(Strophe.serialize(disco_iq)).toBe(
  44. `<iq from="romeo@montague.lit" id="${disco_iq.getAttribute('id')}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+
  45. `<pubsub xmlns="http://jabber.org/protocol/pubsub"><items node="eu.siacs.conversations.axolotl.devicelist"/></pubsub></iq>`);
  46. iq = IQ_stanzas.pop();
  47. expect(Strophe.serialize(iq)).toBe(
  48. `<iq from="romeo@montague.lit/orchard" id="${iq.getAttribute('id')}" to="montague.lit" type="get" xmlns="jabber:client">`+
  49. `<query xmlns="http://jabber.org/protocol/disco#info"/></iq>`);
  50. expect(sent_stanzas.filter(s => (s.nodeName === 'r')).length).toBe(2);
  51. expect(_converse.session.get('unacked_stanzas').length).toBe(5);
  52. // test handling of acks
  53. let ack = u.toStanza(`<a xmlns="urn:xmpp:sm:3" h="2"/>`);
  54. _converse.connection._dataRecv(test_utils.createRequest(ack));
  55. expect(_converse.session.get('unacked_stanzas').length).toBe(3);
  56. // test handling of ack requests
  57. let r = u.toStanza(`<r xmlns="urn:xmpp:sm:3"/>`);
  58. _converse.connection._dataRecv(test_utils.createRequest(r));
  59. ack = await u.waitUntil(() => sent_stanzas.filter(s => (s.nodeName === 'a')).pop());
  60. expect(Strophe.serialize(ack)).toBe('<a h="1" xmlns="urn:xmpp:sm:3"/>');
  61. const disco_result = $iq({
  62. 'type': 'result',
  63. 'from': 'montague.lit',
  64. 'to': 'romeo@montague.lit/orchard',
  65. 'id': disco_iq.getAttribute('id'),
  66. }).c('query', {'xmlns': 'http://jabber.org/protocol/disco#info'})
  67. .c('identity', {
  68. 'category': 'server',
  69. 'type': 'im'
  70. }).up()
  71. .c('feature', {'var': 'http://jabber.org/protocol/disco#info'}).up()
  72. .c('feature', {'var': 'http://jabber.org/protocol/disco#items'});
  73. _converse.connection._dataRecv(test_utils.createRequest(disco_result));
  74. ack = u.toStanza(`<a xmlns="urn:xmpp:sm:3" h="3"/>`);
  75. _converse.connection._dataRecv(test_utils.createRequest(ack));
  76. expect(_converse.session.get('unacked_stanzas').length).toBe(2);
  77. r = u.toStanza(`<r xmlns="urn:xmpp:sm:3"/>`);
  78. _converse.connection._dataRecv(test_utils.createRequest(r));
  79. ack = await u.waitUntil(() => sent_stanzas.filter(s => (s.nodeName === 'a' && s.getAttribute('h') === '1')).pop());
  80. expect(Strophe.serialize(ack)).toBe('<a h="1" xmlns="urn:xmpp:sm:3"/>');
  81. // test session resumption
  82. _converse.connection.IQ_stanzas = [];
  83. IQ_stanzas = _converse.connection.IQ_stanzas;
  84. _converse.api.connection.reconnect();
  85. stanza = await u.waitUntil(() => sent_stanzas.filter(s => (s.tagName === 'resume')).pop());
  86. expect(Strophe.serialize(stanza)).toEqual('<resume h="2" previd="some-long-sm-id" xmlns="urn:xmpp:sm:3"/>');
  87. result = u.toStanza(`<resumed xmlns="urn:xmpp:sm:3" h="another-sequence-number" previd="some-long-sm-id"/>`);
  88. _converse.connection._dataRecv(test_utils.createRequest(result));
  89. // Another <enable> stanza doesn't get sent out
  90. expect(sent_stanzas.filter(s => (s.tagName === 'enable')).length).toBe(1);
  91. expect(_converse.session.get('smacks_enabled')).toBe(true);
  92. await u.waitUntil(() => IQ_stanzas.length === 1);
  93. // Test that unacked stanzas get resent out
  94. iq = IQ_stanzas.pop();
  95. expect(Strophe.serialize(iq)).toBe(
  96. `<iq from="romeo@montague.lit/orchard" id="${iq.getAttribute('id')}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+
  97. `<query xmlns="http://jabber.org/protocol/disco#info"/></iq>`);
  98. expect(IQ_stanzas.filter(iq => sizzle('query[xmlns="jabber:iq:roster"]', iq).pop()).length).toBe(0);
  99. await _converse.api.waitUntil('statusInitialized');
  100. done();
  101. }));
  102. it("might not resume and the session will then be reset",
  103. mock.initConverse(
  104. ['chatBoxesInitialized'],
  105. { 'auto_login': false,
  106. 'enable_smacks': true,
  107. 'show_controlbox_by_default': true,
  108. 'smacks_max_unacked_stanzas': 2
  109. },
  110. async function (done, _converse) {
  111. const view = _converse.chatboxviews.get('controlbox');
  112. spyOn(view, 'renderControlBoxPane').and.callThrough();
  113. _converse.api.user.login('romeo@montague.lit/orchard', 'secret');
  114. const sent_stanzas = _converse.connection.sent_stanzas;
  115. let stanza = await u.waitUntil(() => sent_stanzas.filter(s => (s.tagName === 'enable')).pop());
  116. expect(Strophe.serialize(stanza)).toEqual('<enable resume="true" xmlns="urn:xmpp:sm:3"/>');
  117. let result = u.toStanza(`<enabled xmlns="urn:xmpp:sm:3" id="some-long-sm-id" resume="true"/>`);
  118. _converse.connection._dataRecv(test_utils.createRequest(result));
  119. await test_utils.waitForRoster(_converse, 'current', 1);
  120. // test session resumption
  121. _converse.api.connection.reconnect();
  122. stanza = await u.waitUntil(() => sent_stanzas.filter(s => (s.tagName === 'resume')).pop());
  123. expect(Strophe.serialize(stanza)).toEqual('<resume h="1" previd="some-long-sm-id" xmlns="urn:xmpp:sm:3"/>');
  124. result = u.toStanza(
  125. `<failed xmlns="urn:xmpp:sm:3" h="another-sequence-number">`+
  126. `<item-not-found xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>`+
  127. `</failed>`);
  128. _converse.connection._dataRecv(test_utils.createRequest(result));
  129. // Session data gets reset
  130. expect(_converse.session.get('smacks_enabled')).toBe(false);
  131. expect(_converse.session.get('num_stanzas_handled')).toBe(0);
  132. expect(_converse.session.get('num_stanzas_handled_by_server')).toBe(0);
  133. expect(_converse.session.get('num_stanzas_since_last_ack')).toBe(0);
  134. expect(_converse.session.get('unacked_stanzas').length).toBe(0);
  135. expect(_converse.session.get('roster_cached')).toBeFalsy();
  136. await u.waitUntil(() => sent_stanzas.filter(s => (s.tagName === 'enable')).length === 2);
  137. stanza = sent_stanzas.filter(s => (s.tagName === 'enable')).pop();
  138. expect(Strophe.serialize(stanza)).toEqual('<enable resume="true" xmlns="urn:xmpp:sm:3"/>');
  139. result = u.toStanza(`<enabled xmlns="urn:xmpp:sm:3" id="another-long-sm-id" resume="true"/>`);
  140. _converse.connection._dataRecv(test_utils.createRequest(result));
  141. expect(_converse.session.get('smacks_enabled')).toBe(true);
  142. // Check that the roster gets fetched
  143. await test_utils.waitForRoster(_converse, 'current', 1);
  144. done();
  145. }));
  146. });
  147. }));