EntityCapabilitiesTest.java

  1package im.conversations.android.xmpp;
  2
  3import static org.hamcrest.CoreMatchers.instanceOf;
  4import static org.hamcrest.MatcherAssert.assertThat;
  5import static org.junit.Assert.assertNull;
  6
  7import eu.siacs.conversations.xml.Element;
  8import eu.siacs.conversations.xmpp.manager.DiscoManager;
  9import im.conversations.android.xml.XmlElementReader;
 10import im.conversations.android.xmpp.model.disco.info.InfoQuery;
 11import im.conversations.android.xmpp.model.stanza.Iq;
 12import java.io.IOException;
 13import java.nio.charset.StandardCharsets;
 14import org.junit.Assert;
 15import org.junit.Test;
 16import org.junit.runner.RunWith;
 17import org.robolectric.RobolectricTestRunner;
 18import org.robolectric.annotation.ConscryptMode;
 19
 20@RunWith(RobolectricTestRunner.class)
 21@ConscryptMode(ConscryptMode.Mode.OFF)
 22public class EntityCapabilitiesTest {
 23
 24    @Test
 25    public void entityCaps() throws IOException {
 26        final String xml =
 27                """
 28                <query xmlns='http://jabber.org/protocol/disco#info'
 29                         node='http://code.google.com/p/exodus#QgayPKawpkPSDYmwT/WM94uAlu0='>
 30                    <identity category='client' name='Exodus 0.9.1' type='pc'/>
 31                    <feature var='http://jabber.org/protocol/caps'/>
 32                    <feature var='http://jabber.org/protocol/disco#items'/>
 33                    <feature var='http://jabber.org/protocol/disco#info'/>
 34                    <feature var='http://jabber.org/protocol/muc'/>
 35                  </query>""";
 36        final Element element = XmlElementReader.read(xml.getBytes(StandardCharsets.UTF_8));
 37        assertThat(element, instanceOf(InfoQuery.class));
 38        final InfoQuery info = (InfoQuery) element;
 39        final String var = EntityCapabilities.hash(info).encoded();
 40        Assert.assertEquals("QgayPKawpkPSDYmwT/WM94uAlu0=", var);
 41    }
 42
 43    @Test
 44    public void entityCapsComplexExample() throws IOException {
 45        final String xml =
 46                """
 47                <query xmlns='http://jabber.org/protocol/disco#info'
 48                         node='http://psi-im.org#q07IKJEyjvHSyhy//CH0CxmKi8w='>
 49                    <identity xml:lang='en' category='client' name='Psi 0.11' type='pc'/>
 50                    <identity xml:lang='el' category='client' name='Ψ 0.11' type='pc'/>
 51                    <feature var='http://jabber.org/protocol/caps'/>
 52                    <feature var='http://jabber.org/protocol/disco#info'/>
 53                    <feature var='http://jabber.org/protocol/disco#items'/>
 54                    <feature var='http://jabber.org/protocol/muc'/>
 55                    <x xmlns='jabber:x:data' type='result'>
 56                      <field var='FORM_TYPE' type='hidden'>
 57                        <value>urn:xmpp:dataforms:softwareinfo</value>
 58                      </field>
 59                      <field var='ip_version' type='text-multi' >
 60                        <value>ipv4</value>
 61                        <value>ipv6</value>
 62                      </field>
 63                      <field var='os'>
 64                        <value>Mac</value>
 65                      </field>
 66                      <field var='os_version'>
 67                        <value>10.5.1</value>
 68                      </field>
 69                      <field var='software'>
 70                        <value>Psi</value>
 71                      </field>
 72                      <field var='software_version'>
 73                        <value>0.11</value>
 74                      </field>
 75                    </x>
 76                  </query>""";
 77        final Element element = XmlElementReader.read(xml.getBytes(StandardCharsets.UTF_8));
 78        assertThat(element, instanceOf(InfoQuery.class));
 79        final InfoQuery info = (InfoQuery) element;
 80        final String var = EntityCapabilities.hash(info).encoded();
 81        Assert.assertEquals("q07IKJEyjvHSyhy//CH0CxmKi8w=", var);
 82    }
 83
 84    @Test
 85    public void entityCapsOpenFireOrg() throws IOException {
 86        final String xml =
 87                """
 88  <iq type="result" xmlns="jabber:client" to="inputmice3@igniterealtime.org/Conversations.cI4W" from="igniterealtime.org" id="L3xl8X8_kzvx">
 89  <query node="https://www.igniterealtime.org/projects/openfire/#Cd91QBSG4JGOCEvRsSz64xeJPMk=" xmlns="http://jabber.org/protocol/disco#info">
 90    <identity name="Openfire Server" type="im" category="server" xmlns="http://jabber.org/protocol/disco#info"/>
 91    <identity type="pep" category="pubsub" xmlns="http://jabber.org/protocol/disco#info"/>
 92    <feature var="urn:xmpp:raa:0#embed-presence-directed" xmlns="http://jabber.org/protocol/disco#info"/>
 93    <feature var="http://jabber.org/protocol/caps" xmlns="http://jabber.org/protocol/disco#info"/>
 94    <feature var="http://jabber.org/protocol/pubsub#retrieve-default" xmlns="http://jabber.org/protocol/disco#info"/>
 95    <feature var="http://jabber.org/protocol/pubsub#purge-nodes" xmlns="http://jabber.org/protocol/disco#info"/>
 96    <feature var="http://jabber.org/protocol/pubsub#subscription-options" xmlns="http://jabber.org/protocol/disco#info"/>
 97    <feature var="urn:xmpp:raa:0#embed-message " xmlns="http://jabber.org/protocol/disco#info"/>
 98    <feature var="http://jabber.org/protocol/pubsub#outcast-affiliation" xmlns="http://jabber.org/protocol/disco#info"/>
 99    <feature var="msgoffline" xmlns="http://jabber.org/protocol/disco#info"/>
100    <feature var="http://jabber.org/protocol/pubsub#delete-nodes" xmlns="http://jabber.org/protocol/disco#info"/>
101    <feature var="jabber:iq:register" xmlns="http://jabber.org/protocol/disco#info"/>
102    <feature var="http://jabber.org/protocol/pubsub#config-node" xmlns="http://jabber.org/protocol/disco#info"/>
103    <feature var="http://jabber.org/protocol/pubsub#retrieve-items" xmlns="http://jabber.org/protocol/disco#info"/>
104    <feature var="http://jabber.org/protocol/pubsub#auto-create" xmlns="http://jabber.org/protocol/disco#info"/>
105    <feature var="http://jabber.org/protocol/disco#items" xmlns="http://jabber.org/protocol/disco#info"/>
106    <feature var="http://jabber.org/protocol/pubsub#delete-items" xmlns="http://jabber.org/protocol/disco#info"/>
107    <feature var="urn:xmpp:mam:0" xmlns="http://jabber.org/protocol/disco#info"/>
108    <feature var="urn:xmpp:mam:1" xmlns="http://jabber.org/protocol/disco#info"/>
109    <feature var="urn:xmpp:mam:2" xmlns="http://jabber.org/protocol/disco#info"/>
110    <feature var="urn:xmpp:fulltext:0" xmlns="http://jabber.org/protocol/disco#info"/>
111    <feature var="http://jabber.org/protocol/pubsub#persistent-items" xmlns="http://jabber.org/protocol/disco#info"/>
112    <feature var="http://jabber.org/protocol/pubsub#create-and-configure" xmlns="http://jabber.org/protocol/disco#info"/>
113    <feature var="http://jabber.org/protocol/pubsub#retrieve-affiliations" xmlns="http://jabber.org/protocol/disco#info"/>
114    <feature var="urn:xmpp:time" xmlns="http://jabber.org/protocol/disco#info"/>
115    <feature var="http://jabber.org/protocol/pubsub#manage-subscriptions" xmlns="http://jabber.org/protocol/disco#info"/>
116    <feature var="urn:xmpp:bookmarks-conversion:0" xmlns="http://jabber.org/protocol/disco#info"/>
117    <feature var="http://jabber.org/protocol/pubsub#auto-subscribe" xmlns="http://jabber.org/protocol/disco#info"/>
118    <feature var="http://jabber.org/protocol/offline" xmlns="http://jabber.org/protocol/disco#info"/>
119    <feature var="http://jabber.org/protocol/pubsub#publish-options" xmlns="http://jabber.org/protocol/disco#info"/>
120    <feature var="urn:xmpp:carbons:2" xmlns="http://jabber.org/protocol/disco#info"/>
121    <feature var="http://jabber.org/protocol/address" xmlns="http://jabber.org/protocol/disco#info"/>
122    <feature var="http://jabber.org/protocol/pubsub#collections" xmlns="http://jabber.org/protocol/disco#info"/>
123    <feature var="http://jabber.org/protocol/pubsub#retrieve-subscriptions" xmlns="http://jabber.org/protocol/disco#info"/>
124    <feature var="vcard-temp" xmlns="http://jabber.org/protocol/disco#info"/>
125    <feature var="http://jabber.org/protocol/pubsub#subscribe" xmlns="http://jabber.org/protocol/disco#info"/>
126    <feature var="http://jabber.org/protocol/pubsub#create-nodes" xmlns="http://jabber.org/protocol/disco#info"/>
127    <feature var="http://jabber.org/protocol/pubsub#get-pending" xmlns="http://jabber.org/protocol/disco#info"/>
128    <feature var="urn:xmpp:blocking" xmlns="http://jabber.org/protocol/disco#info"/>
129    <feature var="http://jabber.org/protocol/pubsub#multi-subscribe" xmlns="http://jabber.org/protocol/disco#info"/>
130    <feature var="http://jabber.org/protocol/pubsub#presence-notifications" xmlns="http://jabber.org/protocol/disco#info"/>
131    <feature var="urn:xmpp:ping" xmlns="http://jabber.org/protocol/disco#info"/>
132    <feature var="urn:xmpp:archive:manage" xmlns="http://jabber.org/protocol/disco#info"/>
133    <feature var="http://jabber.org/protocol/pubsub#filtered-notifications" xmlns="http://jabber.org/protocol/disco#info"/>
134    <feature var="urn:xmpp:push:0" xmlns="http://jabber.org/protocol/disco#info"/>
135    <feature var="http://jabber.org/protocol/pubsub#meta-data" xmlns="http://jabber.org/protocol/disco#info"/>
136    <feature var="http://jabber.org/protocol/pubsub#multi-items" xmlns="http://jabber.org/protocol/disco#info"/>
137    <feature var="http://jabber.org/protocol/pubsub#item-ids" xmlns="http://jabber.org/protocol/disco#info"/>
138    <feature var="jabber:iq:roster" xmlns="http://jabber.org/protocol/disco#info"/>
139    <feature var="http://jabber.org/protocol/pubsub#instant-nodes" xmlns="http://jabber.org/protocol/disco#info"/>
140    <feature var="http://jabber.org/protocol/pubsub#modify-affiliations" xmlns="http://jabber.org/protocol/disco#info"/>
141    <feature var="urn:xmpp:raa:0#embed-presence-sub" xmlns="http://jabber.org/protocol/disco#info"/>
142    <feature var="http://jabber.org/protocol/pubsub" xmlns="http://jabber.org/protocol/disco#info"/>
143    <feature var="http://jabber.org/protocol/pubsub#publisher-affiliation" xmlns="http://jabber.org/protocol/disco#info"/>
144    <feature var="http://jabber.org/protocol/pubsub#access-open" xmlns="http://jabber.org/protocol/disco#info"/>
145    <feature var="jabber:iq:version" xmlns="http://jabber.org/protocol/disco#info"/>
146    <feature var="http://jabber.org/protocol/pubsub#retract-items" xmlns="http://jabber.org/protocol/disco#info"/>
147    <feature var="urn:xmpp:extdisco:1" xmlns="http://jabber.org/protocol/disco#info"/>
148    <feature var="jabber:iq:privacy" xmlns="http://jabber.org/protocol/disco#info"/>
149    <feature var="urn:xmpp:extdisco:2" xmlns="http://jabber.org/protocol/disco#info"/>
150    <feature var="http://jabber.org/protocol/commands" xmlns="http://jabber.org/protocol/disco#info"/>
151    <feature var="jabber:iq:last" xmlns="http://jabber.org/protocol/disco#info"/>
152    <feature var="urn:xmpp:raa:0" xmlns="http://jabber.org/protocol/disco#info"/>
153    <feature var="http://jabber.org/protocol/pubsub#publish" xmlns="http://jabber.org/protocol/disco#info"/>
154    <feature var="urn:xmpp:serverinfo:0" xmlns="http://jabber.org/protocol/disco#info"/>
155    <feature var="urn:xmpp:archive:auto" xmlns="http://jabber.org/protocol/disco#info"/>
156    <feature var="http://jabber.org/protocol/disco#info" xmlns="http://jabber.org/protocol/disco#info"/>
157    <feature var="jabber:iq:private" xmlns="http://jabber.org/protocol/disco#info"/>
158    <feature var="http://jabber.org/protocol/rsm" xmlns="http://jabber.org/protocol/disco#info"/>
159    <x type="result" xmlns="jabber:x:data">
160      <field var="FORM_TYPE" type="hidden" xmlns="jabber:x:data">
161        <value xmlns="jabber:x:data">urn:xmpp:dataforms:softwareinfo</value>
162      </field>
163      <field var="os" type="text-single" xmlns="jabber:x:data">
164        <value xmlns="jabber:x:data">Linux</value>
165      </field>
166      <field var="os_version" type="text-single" xmlns="jabber:x:data">
167        <value xmlns="jabber:x:data">4.14.355-276.618.amzn2.x86_64 amd64 - Java 17.0.14</value>
168      </field>
169      <field var="software" type="text-single" xmlns="jabber:x:data">
170        <value xmlns="jabber:x:data">Openfire</value>
171      </field>
172      <field var="software_version" type="text-single" xmlns="jabber:x:data">
173        <value xmlns="jabber:x:data">5.0.0 Alpha</value>
174      </field>
175    </x>
176    <x type="result" xmlns="jabber:x:data">
177      <field var="FORM_TYPE" type="hidden" xmlns="jabber:x:data">
178        <value xmlns="jabber:x:data">http://jabber.org/network/serverinfo</value>
179      </field>
180      <field var="admin-addresses" type="list-multi" xmlns="jabber:x:data">
181        <value xmlns="jabber:x:data">xmpp:dwd@dave.cridland.net</value>
182        <value xmlns="jabber:x:data">xmpp:akrherz@igniterealtime.org</value>
183        <value xmlns="jabber:x:data">xmpp:benjamin@igniterealtime.org</value>
184        <value xmlns="jabber:x:data">mailto:benjamin@holyarmy.org</value>
185        <value xmlns="jabber:x:data">xmpp:csh@igniterealtime.org</value>
186        <value xmlns="jabber:x:data">xmpp:dan.caseley@igniterealtime.org</value>
187        <value xmlns="jabber:x:data">mailto:dan.caseley@surevine.com</value>
188        <value xmlns="jabber:x:data">xmpp:flow@igniterealtime.org</value>
189        <value xmlns="jabber:x:data">xmpp:gdt@igniterealtime.org</value>
190        <value xmlns="jabber:x:data">mailto:greg.d.thomas@gmail.com</value>
191        <value xmlns="jabber:x:data">xmpp:guus.der.kinderen@igniterealtime.org</value>
192        <value xmlns="jabber:x:data">mailto:guus.der.kinderen@gmail.com</value>
193        <value xmlns="jabber:x:data">xmpp:lg@igniterealtime.org</value>
194        <value xmlns="jabber:x:data">xmpp:rcollier@igniterealtime.org</value>
195        <value xmlns="jabber:x:data">mailto:robincollier@hotmail.com</value>
196      </field>
197    </x>
198  </query>
199</iq>
200""";
201        final Element element = XmlElementReader.read(xml.getBytes(StandardCharsets.UTF_8));
202        assertThat(element, instanceOf(Iq.class));
203        final var iq = (Iq) element;
204        final InfoQuery info = iq.getExtension(InfoQuery.class);
205        final String var = EntityCapabilities.hash(info).encoded();
206        Assert.assertEquals("Cd91QBSG4JGOCEvRsSz64xeJPMk=", var);
207    }
208
209    @Test
210    public void entityCapsOpenFireTestServer() throws IOException {
211        final String xml =
212                """
213<iq type="result" id="779-6" to="jane@example.org" xmlns="jabber:client">
214  <query xmlns="http://jabber.org/protocol/disco#info">
215    <identity category="server" name="Openfire Server" type="im"/>
216    <identity category="pubsub" type="pep"/>
217    <feature var="http://jabber.org/protocol/caps"/>
218    <feature var="http://jabber.org/protocol/pubsub#retrieve-default"/>
219    <feature var="http://jabber.org/protocol/pubsub#purge-nodes"/>
220    <feature var="http://jabber.org/protocol/pubsub#subscription-options"/>
221    <feature var="http://jabber.org/protocol/pubsub#outcast-affiliation"/>
222    <feature var="msgoffline"/>
223    <feature var="jabber:iq:register"/>
224    <feature var="http://jabber.org/protocol/pubsub#delete-nodes"/>
225    <feature var="http://jabber.org/protocol/pubsub#config-node"/>
226    <feature var="http://jabber.org/protocol/pubsub#retrieve-items"/>
227    <feature var="http://jabber.org/protocol/pubsub#auto-create"/>
228    <feature var="http://jabber.org/protocol/pubsub#delete-items"/>
229    <feature var="http://jabber.org/protocol/disco#items"/>
230    <feature var="http://jabber.org/protocol/pubsub#persistent-items"/>
231    <feature var="http://jabber.org/protocol/pubsub#create-and-configure"/>
232    <feature var="http://jabber.org/protocol/pubsub#retrieve-affiliations"/>
233    <feature var="urn:xmpp:time"/>
234    <feature var="http://jabber.org/protocol/pubsub#manage-subscriptions"/>
235    <feature var="urn:xmpp:bookmarks-conversion:0"/>
236    <feature var="http://jabber.org/protocol/offline"/>
237    <feature var="http://jabber.org/protocol/pubsub#auto-subscribe"/>
238    <feature var="http://jabber.org/protocol/pubsub#publish-options"/>
239    <feature var="urn:xmpp:carbons:2"/>
240    <feature var="http://jabber.org/protocol/address"/>
241    <feature var="http://jabber.org/protocol/pubsub#collections"/>
242    <feature var="http://jabber.org/protocol/pubsub#retrieve-subscriptions"/>
243    <feature var="vcard-temp"/>
244    <feature var="http://jabber.org/protocol/pubsub#subscribe"/>
245    <feature var="http://jabber.org/protocol/pubsub#create-nodes"/>
246    <feature var="http://jabber.org/protocol/pubsub#get-pending"/>
247    <feature var="urn:xmpp:blocking"/>
248    <feature var="http://jabber.org/protocol/pubsub#multi-subscribe"/>
249    <feature var="http://jabber.org/protocol/pubsub#presence-notifications"/>
250    <feature var="urn:xmpp:ping"/>
251    <feature var="http://jabber.org/protocol/pubsub#filtered-notifications"/>
252    <feature var="http://jabber.org/protocol/pubsub#item-ids"/>
253    <feature var="http://jabber.org/protocol/pubsub#meta-data"/>
254    <feature var="http://jabber.org/protocol/pubsub#multi-items"/>
255    <feature var="jabber:iq:roster"/>
256    <feature var="http://jabber.org/protocol/pubsub#instant-nodes"/>
257    <feature var="http://jabber.org/protocol/pubsub#modify-affiliations"/>
258    <feature var="http://jabber.org/protocol/pubsub"/>
259    <feature var="http://jabber.org/protocol/pubsub#publisher-affiliation"/>
260    <feature var="http://jabber.org/protocol/pubsub#access-open"/>
261    <feature var="jabber:iq:version"/>
262    <feature var="http://jabber.org/protocol/pubsub#retract-items"/>
263    <feature var="jabber:iq:privacy"/>
264    <feature var="jabber:iq:last"/>
265    <feature var="http://jabber.org/protocol/commands"/>
266    <feature var="http://jabber.org/protocol/pubsub#publish"/>
267    <feature var="http://jabber.org/protocol/disco#info"/>
268    <feature var="jabber:iq:private"/>
269    <feature var="http://jabber.org/protocol/rsm"/>
270    <x xmlns="jabber:x:data" type="result">
271      <field var="FORM_TYPE" type="hidden">
272        <value>http://jabber.org/network/serverinfo</value>
273      </field>
274      <field var="admin-addresses" type="list-multi">
275        <value>xmpp:admin@example.org</value>
276        <value>mailto:admin@example.com</value>
277      </field>
278    </x>
279    <x xmlns="jabber:x:data" type="result">
280      <field var="FORM_TYPE" type="hidden">
281        <value>urn:xmpp:dataforms:softwareinfo</value>
282      </field>
283      <field type="text-single" var="os">
284        <value>Linux</value>
285      </field>
286      <field type="text-single" var="os_version">
287        <value>6.8.0-59-generic amd64 - Java 21.0.7</value>
288      </field>
289      <field type="text-single" var="software">
290        <value>Openfire</value>
291      </field>
292      <field type="text-single" var="software_version">
293        <value>5.0.0 Alpha</value>
294      </field>
295    </x>
296  </query>
297</iq>
298""";
299        final Element element = XmlElementReader.read(xml.getBytes(StandardCharsets.UTF_8));
300        assertThat(element, instanceOf(Iq.class));
301        final var iq = (Iq) element;
302        final InfoQuery info = iq.getExtension(InfoQuery.class);
303        final String var = EntityCapabilities.hash(info).encoded();
304        Assert.assertEquals("3wkXXN9QL/i/AyVoHaqaiTT8BFA=", var);
305    }
306
307    @Test
308    public void caps2() throws IOException {
309        final String xml =
310                """
311                <query xmlns="http://jabber.org/protocol/disco#info">
312                  <identity category="client" name="BombusMod" type="mobile"/>
313                  <feature var="http://jabber.org/protocol/si"/>
314                  <feature var="http://jabber.org/protocol/bytestreams"/>
315                  <feature var="http://jabber.org/protocol/chatstates"/>
316                  <feature var="http://jabber.org/protocol/disco#info"/>
317                  <feature var="http://jabber.org/protocol/disco#items"/>
318                  <feature var="urn:xmpp:ping"/>
319                  <feature var="jabber:iq:time"/>
320                  <feature var="jabber:iq:privacy"/>
321                  <feature var="jabber:iq:version"/>
322                  <feature var="http://jabber.org/protocol/rosterx"/>
323                  <feature var="urn:xmpp:time"/>
324                  <feature var="jabber:x:oob"/>
325                  <feature var="http://jabber.org/protocol/ibb"/>
326                  <feature var="http://jabber.org/protocol/si/profile/file-transfer"/>
327                  <feature var="urn:xmpp:receipts"/>
328                  <feature var="jabber:iq:roster"/>
329                  <feature var="jabber:iq:last"/>
330                </query>""";
331        final Element element = XmlElementReader.read(xml.getBytes(StandardCharsets.UTF_8));
332        assertThat(element, instanceOf(InfoQuery.class));
333        final InfoQuery info = (InfoQuery) element;
334        final String var = EntityCapabilities2.hash(info).encoded();
335        Assert.assertEquals("kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8=", var);
336    }
337
338    @Test
339    public void caps2complex() throws IOException {
340        final String xml =
341                """
342                <query xmlns="http://jabber.org/protocol/disco#info">
343                  <identity category="client" name="Tkabber" type="pc" xml:lang="en"/>
344                  <identity category="client" name="Ткаббер" type="pc" xml:lang="ru"/>
345                  <feature var="games:board"/>
346                  <feature var="http://jabber.org/protocol/activity"/>
347                  <feature var="http://jabber.org/protocol/activity+notify"/>
348                  <feature var="http://jabber.org/protocol/bytestreams"/>
349                  <feature var="http://jabber.org/protocol/chatstates"/>
350                  <feature var="http://jabber.org/protocol/commands"/>
351                  <feature var="http://jabber.org/protocol/disco#info"/>
352                  <feature var="http://jabber.org/protocol/disco#items"/>
353                  <feature var="http://jabber.org/protocol/evil"/>
354                  <feature var="http://jabber.org/protocol/feature-neg"/>
355                  <feature var="http://jabber.org/protocol/geoloc"/>
356                  <feature var="http://jabber.org/protocol/geoloc+notify"/>
357                  <feature var="http://jabber.org/protocol/ibb"/>
358                  <feature var="http://jabber.org/protocol/iqibb"/>
359                  <feature var="http://jabber.org/protocol/mood"/>
360                  <feature var="http://jabber.org/protocol/mood+notify"/>
361                  <feature var="http://jabber.org/protocol/rosterx"/>
362                  <feature var="http://jabber.org/protocol/si"/>
363                  <feature var="http://jabber.org/protocol/si/profile/file-transfer"/>
364                  <feature var="http://jabber.org/protocol/tune"/>
365                  <feature var="http://www.facebook.com/xmpp/messages"/>
366                  <feature var="http://www.xmpp.org/extensions/xep-0084.html#ns-metadata+notify"/>
367                  <feature var="jabber:iq:avatar"/>
368                  <feature var="jabber:iq:browse"/>
369                  <feature var="jabber:iq:dtcp"/>
370                  <feature var="jabber:iq:filexfer"/>
371                  <feature var="jabber:iq:ibb"/>
372                  <feature var="jabber:iq:inband"/>
373                  <feature var="jabber:iq:jidlink"/>
374                  <feature var="jabber:iq:last"/>
375                  <feature var="jabber:iq:oob"/>
376                  <feature var="jabber:iq:privacy"/>
377                  <feature var="jabber:iq:roster"/>
378                  <feature var="jabber:iq:time"/>
379                  <feature var="jabber:iq:version"/>
380                  <feature var="jabber:x:data"/>
381                  <feature var="jabber:x:event"/>
382                  <feature var="jabber:x:oob"/>
383                  <feature var="urn:xmpp:avatar:metadata+notify"/>
384                  <feature var="urn:xmpp:ping"/>
385                  <feature var="urn:xmpp:receipts"/>
386                  <feature var="urn:xmpp:time"/>
387                  <x xmlns="jabber:x:data" type="result">
388                    <field type="hidden" var="FORM_TYPE">
389                      <value>urn:xmpp:dataforms:softwareinfo</value>
390                    </field>
391                    <field var="software">
392                      <value>Tkabber</value>
393                    </field>
394                    <field var="software_version">
395                      <value>0.11.1-svn-20111216-mod (Tcl/Tk 8.6b2)</value>
396                    </field>
397                    <field var="os">
398                      <value>Windows</value>
399                    </field>
400                    <field var="os_version">
401                      <value>XP</value>
402                    </field>
403                  </x>
404                </query>""";
405        final Element element = XmlElementReader.read(xml.getBytes(StandardCharsets.UTF_8));
406        assertThat(element, instanceOf(InfoQuery.class));
407        final InfoQuery info = (InfoQuery) element;
408        final String var = EntityCapabilities2.hash(info).encoded();
409        Assert.assertEquals("u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY=", var);
410    }
411
412    @Test
413    public void parseCaps2Node() {
414        final var caps =
415                DiscoManager.buildHashFromNode(
416                        "urn:xmpp:caps#sha-256.u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY=");
417        assertThat(caps, instanceOf(EntityCapabilities2.EntityCaps2Hash.class));
418    }
419
420    @Test
421    public void parseCaps2NodeMissingHash() {
422        final var caps = DiscoManager.buildHashFromNode("urn:xmpp:caps#sha-256.");
423        assertNull(caps);
424    }
425
426    @Test
427    public void parseCaps2NodeInvalid() {
428        final var caps = DiscoManager.buildHashFromNode("urn:xmpp:caps#-");
429        assertNull(caps);
430    }
431
432    @Test
433    public void parseCaps2NodeUnknownAlgo() {
434        final var caps = DiscoManager.buildHashFromNode("urn:xmpp:caps#test.test");
435        assertNull(caps);
436    }
437}