comparison tests/unit/test_plugin_xep_0176.py @ 4046:0e3ce379aae3

tests (unit): tests for plugin XEP-0176: fix 419
author Goffi <goffi@goffi.org>
date Mon, 15 May 2023 16:23:50 +0200
parents
children 4b842c1fb686
comparison
equal deleted inserted replaced
4045:ae756bf7c3e8 4046:0e3ce379aae3
1 #!/usr/bin/env python3
2
3 # Libervia: an XMPP client
4 # Copyright (C) 2009-2023 Jérôme Poisson (goffi@goffi.org)
5
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Affero General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Affero General Public License for more details.
15
16 # You should have received a copy of the GNU Affero General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19 from unittest.mock import AsyncMock, MagicMock
20
21 from pytest import fixture
22 from pytest_twisted import ensureDeferred as ed
23
24 from sat.plugins.plugin_xep_0166 import XEP_0166
25 from sat.plugins.plugin_xep_0176 import NS_JINGLE_ICE_UDP, XEP_0176
26 from sat.tools import xml_tools
27
28
29 @fixture(autouse=True)
30 def no_transport_register(monkeypatch):
31 """Do not register the transport in XEP-0166"""
32 monkeypatch.setattr(XEP_0166, "register_transport", lambda *a, **kw: None)
33
34
35 class TestXEP0176:
36 def create_mock_session(self, content_name):
37 return {
38 "id": "test-session-id",
39 "contents": {
40 content_name: {
41 "application_data": {"media": "audio"},
42 "transport_data": {
43 "local_ice_data": {
44 "ufrag": "testufrag",
45 "pwd": "testpwd",
46 "candidates": [
47 {
48 "id": "candidate_1",
49 "component_id": 1,
50 "foundation": "1",
51 "address": "192.0.2.1",
52 "port": 1234,
53 "priority": 1,
54 "transport": "udp",
55 "type": "host",
56 }
57 ],
58 }
59 },
60 }
61 },
62 }
63
64 def test_build_transport(self, host, monkeypatch):
65 """ICE data is correctly transformed into transport element"""
66 xep_0176 = XEP_0176(host)
67
68 ice_data = {
69 "ufrag": "user1",
70 "pwd": "password1",
71 "candidates": [
72 {
73 "component_id": 1,
74 "foundation": "1",
75 "address": "192.168.0.1",
76 "port": 1234,
77 "priority": 100,
78 "transport": "udp",
79 "type": "host",
80 "generation": "0",
81 "network": "0",
82 },
83 {
84 "component_id": 2,
85 "foundation": "1",
86 "address": "192.168.0.2",
87 "port": 5678,
88 "priority": 100,
89 "transport": "udp",
90 "type": "host",
91 "generation": "0",
92 "network": "0",
93 "rel_addr": "10.0.0.1",
94 "rel_port": 9012,
95 },
96 ],
97 }
98
99 transport_elt = xep_0176.build_transport(ice_data)
100
101 assert transport_elt.name == "transport"
102 assert transport_elt.uri == NS_JINGLE_ICE_UDP
103 assert transport_elt.getAttribute("ufrag") == "user1"
104 assert transport_elt.getAttribute("pwd") == "password1"
105
106 candidates = list(transport_elt.elements(NS_JINGLE_ICE_UDP, "candidate"))
107
108 assert len(candidates) == len(ice_data["candidates"])
109
110 for i, candidate_elt in enumerate(candidates):
111 ice_candidate = ice_data["candidates"][i]
112 assert (
113 int(candidate_elt.getAttribute("component"))
114 == ice_candidate["component_id"]
115 )
116 assert candidate_elt.getAttribute("foundation") == ice_candidate["foundation"]
117 assert candidate_elt.getAttribute("ip") == ice_candidate["address"]
118 assert int(candidate_elt.getAttribute("port")) == ice_candidate["port"]
119 assert (
120 int(candidate_elt.getAttribute("priority")) == ice_candidate["priority"]
121 )
122 assert candidate_elt.getAttribute("protocol") == ice_candidate["transport"]
123 assert candidate_elt.getAttribute("type") == ice_candidate["type"]
124 assert candidate_elt.getAttribute("generation") == str(
125 ice_candidate.get("generation", "0")
126 )
127 assert candidate_elt.getAttribute("network") == str(
128 ice_candidate.get("network", "0")
129 )
130
131 if "rel_addr" in ice_candidate:
132 assert candidate_elt.getAttribute("rel-addr") == ice_candidate["rel_addr"]
133 assert (
134 int(candidate_elt.getAttribute("rel-port"))
135 == ice_candidate["rel_port"]
136 )
137 else:
138 assert candidate_elt.getAttribute("rel-addr") is None
139 assert candidate_elt.getAttribute("rel-port") is None
140
141 def test_parse_transport(self, host):
142 """Transport element is correctly parsed into ICE data"""
143 xep_0176 = XEP_0176(host)
144
145 transport_elt = xml_tools.parse(
146 """
147 <transport xmlns="urn:xmpp:jingle:transports:ice-udp:1"
148 pwd="password1"
149 ufrag="user1">
150 <candidate component="1"
151 foundation="1"
152 generation="0"
153 id="uuid1"
154 ip="192.168.0.1"
155 network="0"
156 port="1234"
157 priority="100"
158 protocol="udp"
159 type="host" />
160 <candidate component="2"
161 foundation="1"
162 generation="0"
163 id="uuid2"
164 ip="192.168.0.2"
165 network="0"
166 port="5678"
167 priority="100"
168 protocol="udp"
169 type="host"
170 rel-addr="10.0.0.1"
171 rel-port="9012" />
172 </transport>
173 """
174 )
175
176 ice_data = xep_0176.parse_transport(transport_elt)
177
178 assert transport_elt.getAttribute("ufrag") == "user1"
179 assert transport_elt.getAttribute("pwd") == "password1"
180
181 candidates = list(transport_elt.elements(NS_JINGLE_ICE_UDP, "candidate"))
182 assert len(candidates) == len(ice_data["candidates"])
183
184 for i, candidate_elt in enumerate(candidates):
185 ice_candidate = ice_data["candidates"][i]
186 assert (
187 int(candidate_elt.getAttribute("component"))
188 == ice_candidate["component_id"]
189 )
190 assert candidate_elt.getAttribute("foundation") == ice_candidate["foundation"]
191 assert candidate_elt.getAttribute("ip") == ice_candidate["address"]
192 assert int(candidate_elt.getAttribute("port")) == ice_candidate["port"]
193 assert (
194 int(candidate_elt.getAttribute("priority")) == ice_candidate["priority"]
195 )
196 assert candidate_elt.getAttribute("protocol") == ice_candidate["transport"]
197 assert candidate_elt.getAttribute("type") == ice_candidate["type"]
198 assert candidate_elt.getAttribute("generation") == str(
199 ice_candidate.get("generation", "0")
200 )
201 assert candidate_elt.getAttribute("network") == str(
202 ice_candidate.get("network", "0")
203 )
204
205 if "rel_addr" in ice_candidate:
206 assert candidate_elt.getAttribute("rel-addr") == ice_candidate["rel_addr"]
207 assert (
208 int(candidate_elt.getAttribute("rel-port"))
209 == ice_candidate["rel_port"]
210 )
211 else:
212 assert candidate_elt.getAttribute("rel-addr") is None
213 assert candidate_elt.getAttribute("rel-port") is None
214
215 @ed
216 async def test_jingle_session_init(self, host, client):
217 """<transport/> element is built on initiator side during init"""
218 xep_0176 = XEP_0176(host)
219
220 content_name = "test-content"
221 session = self.create_mock_session(content_name)
222
223 transport_elt = await xep_0176.jingle_session_init(client, session, content_name)
224
225 expected_transport_elt = xep_0176.build_transport(
226 session["contents"][content_name]["transport_data"]["local_ice_data"]
227 )
228
229 assert transport_elt.toXml() == expected_transport_elt.toXml()
230
231 @ed
232 async def test_jingle_handler(self, host, client):
233 """<transport/> element is built on responder side during init"""
234 xep_0176 = XEP_0176(host)
235
236 content_name = "test-content"
237 action = "session-initiate"
238 session = self.create_mock_session(content_name)
239 transport_elt = xml_tools.parse("<transport/>")
240
241 returned_transport_elt = await xep_0176.jingle_handler(
242 client, action, session, content_name, transport_elt
243 )
244
245 expected_transport_elt = xep_0176.build_transport(
246 session["contents"][content_name]["transport_data"]["local_ice_data"]
247 )
248
249 assert returned_transport_elt.toXml() == expected_transport_elt.toXml()
250
251 @ed
252 async def test_ice_candidates_add(self, host, client):
253 """local new ICE candidates are added, IQ is sent, bridge signal emitted"""
254 xep_0176 = XEP_0176(host)
255
256 content_name = "test-content"
257 session_id = "test-session-id"
258 media_ice_data_s = {
259 "audio": {
260 # we use different "ufrag" and "pwd" than in "create_mock_session" to
261 # trigger an ICE restart
262 "ufrag": "new_testufrag",
263 "pwd": "new_testpwd",
264 "candidates": [
265 {
266 "component_id": 2,
267 "foundation": "2",
268 "address": "192.0.2.2",
269 "port": 1235,
270 "priority": 2,
271 "transport": "udp",
272 "type": "host",
273 }
274 ],
275 }
276 }
277
278 mock_session = self.create_mock_session(content_name)
279 xep_0176._j.get_session = MagicMock(return_value=mock_session)
280 xep_0176._j.build_action = MagicMock()
281 iq_elt = AsyncMock()
282 xep_0176._j.build_action.return_value = (iq_elt, None)
283 xep_0176.host.bridge.ice_restart = MagicMock()
284
285 await xep_0176.ice_candidates_add(client, session_id, media_ice_data_s)
286
287 xep_0176._j.build_action.assert_called()
288 iq_elt.send.assert_called()
289 xep_0176.host.bridge.ice_restart.assert_called()
290
291 # Checking that local ICE data is updated correctly
292 updated_local_ice_data = mock_session["contents"][content_name]["transport_data"][
293 "local_ice_data"
294 ]
295 assert updated_local_ice_data["ufrag"] == "new_testufrag"
296 assert updated_local_ice_data["pwd"] == "new_testpwd"
297 assert (
298 updated_local_ice_data["candidates"]
299 == media_ice_data_s["audio"]["candidates"]
300 )