Mercurial > libervia-backend
comparison tests/unit/test_plugin_xep_0420.py @ 3877:00212260f659
plugin XEP-0420: Implementation of Stanza Content Encryption:
Includes implementation of XEP-0082 (XMPP date and time profiles) and tests for both new plugins.
Everything is type checked, linted, format checked and unit tested.
Adds new dependency xmlschema.
fix 377
author | Syndace <me@syndace.dev> |
---|---|
date | Tue, 23 Aug 2022 12:04:11 +0200 |
parents | |
children | 8289ac1b34f4 |
comparison
equal
deleted
inserted
replaced
3876:e3c1f4736ab2 | 3877:00212260f659 |
---|---|
1 #!/usr/bin/env python3 | |
2 | |
3 # Tests for Libervia's Stanza Content Encryption plugin | |
4 # Copyright (C) 2022-2022 Tim Henkes (me@syndace.dev) | |
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 # Type-check with `mypy --strict --disable-error-code no-untyped-call` | |
20 # Lint with `pylint` | |
21 | |
22 from datetime import datetime, timezone | |
23 from typing import Callable, Iterator, Optional, cast | |
24 | |
25 import pytest | |
26 | |
27 from sat.plugins.plugin_xep_0334 import NS_HINTS | |
28 from sat.plugins.plugin_xep_0420 import ( | |
29 NS_SCE, XEP_0420, AffixVerificationFailed, ProfileRequirementsNotMet, SCEAffixPolicy, | |
30 SCECustomAffix, SCEProfile | |
31 ) | |
32 from sat.tools.xml_tools import ElementParser | |
33 from twisted.words.xish import domish | |
34 | |
35 | |
36 __all__ = [ # pylint: disable=unused-variable | |
37 "test_unpack_matches_original", | |
38 "test_affixes_included", | |
39 "test_all_affixes_verified", | |
40 "test_incomplete_affixes", | |
41 "test_rpad_affix", | |
42 "test_time_affix", | |
43 "test_to_affix", | |
44 "test_from_affix", | |
45 "test_custom_affixes", | |
46 "test_namespace_conversion", | |
47 "test_non_encryptable_elements", | |
48 "test_schema_validation" | |
49 ] | |
50 | |
51 | |
52 string_to_domish = cast(Callable[[str], domish.Element], ElementParser()) | |
53 | |
54 | |
55 class CustomAffixImpl(SCECustomAffix): | |
56 """ | |
57 A simple custom affix implementation for testing purposes. Verifies the full JIDs of | |
58 both sender and recipient. | |
59 | |
60 @warning: This is just an example, an affix element like this might not make sense due | |
61 to potentially allowed modifications of recipient/sender full JIDs (I don't know | |
62 enough about XMPP routing to know whether full JIDs are always left untouched by | |
63 the server). | |
64 """ | |
65 | |
66 @property | |
67 def element_name(self) -> str: | |
68 return "full-jids" | |
69 | |
70 @property | |
71 def element_schema(self) -> str: | |
72 return """<xs:element name="full-jids"> | |
73 <xs:complexType> | |
74 <xs:attribute name="recipient" type="xs:string"/> | |
75 <xs:attribute name="sender" type="xs:string"/> | |
76 </xs:complexType> | |
77 </xs:element>""" | |
78 | |
79 def create(self, stanza: domish.Element) -> domish.Element: | |
80 recipient = cast(Optional[str], stanza.getAttribute("to", None)) | |
81 sender = cast(Optional[str], stanza.getAttribute("from", None)) | |
82 | |
83 if recipient is None or sender is None: | |
84 raise ValueError( | |
85 "Stanza doesn't have ``to`` and ``from`` attributes required by the" | |
86 " full-jids custom affix." | |
87 ) | |
88 | |
89 element = domish.Element((NS_SCE, "full-jids")) | |
90 element["recipient"] = recipient | |
91 element["sender"] = sender | |
92 return element | |
93 | |
94 def verify(self, stanza: domish.Element, element: domish.Element) -> None: | |
95 recipient_target = element["recipient"] | |
96 recipient_actual = stanza.getAttribute("to") | |
97 | |
98 sender_target = element["sender"] | |
99 sender_actual = stanza.getAttribute("from") | |
100 | |
101 if recipient_actual != recipient_target or sender_actual != sender_target: | |
102 raise AffixVerificationFailed( | |
103 f"Full JIDs differ. Recipient: actual={recipient_actual} vs." | |
104 f" target={recipient_target}; Sender: actual={sender_actual} vs." | |
105 f" target={sender_target}" | |
106 ) | |
107 | |
108 | |
109 def test_unpack_matches_original() -> None: # pylint: disable=missing-function-docstring | |
110 profile = SCEProfile( | |
111 SCEAffixPolicy.REQUIRED, | |
112 SCEAffixPolicy.OPTIONAL, | |
113 SCEAffixPolicy.NOT_NEEDED, | |
114 SCEAffixPolicy.OPTIONAL, | |
115 custom_policies={ CustomAffixImpl(): SCEAffixPolicy.NOT_NEEDED } | |
116 ) | |
117 | |
118 stanza_string = ( | |
119 '<message from="foo@example.com" to="bar@example.com"><body>Test with both a body' | |
120 ' and some other custom element.</body><custom xmlns="urn:xmpp:example:0"' | |
121 ' test="matches-original">some more content</custom></message>' | |
122 ) | |
123 | |
124 stanza = string_to_domish(stanza_string) | |
125 | |
126 envelope_serialized = XEP_0420.pack_stanza(profile, stanza) | |
127 | |
128 # The stanza should not have child elements any more | |
129 assert len(list(stanza.elements())) == 0 | |
130 | |
131 XEP_0420.unpack_stanza(profile, stanza, envelope_serialized) | |
132 | |
133 # domish.Element doesn't override __eq__, thus we compare the .toXml() strings here in | |
134 # the hope that serialization for an example as small as this is unique enough to be | |
135 # compared that way. | |
136 assert stanza.toXml() == string_to_domish(stanza_string).toXml() | |
137 | |
138 | |
139 def test_affixes_included() -> None: # pylint: disable=missing-function-docstring | |
140 custom_affix = CustomAffixImpl() | |
141 | |
142 profile = SCEProfile( | |
143 SCEAffixPolicy.REQUIRED, | |
144 SCEAffixPolicy.OPTIONAL, | |
145 SCEAffixPolicy.NOT_NEEDED, | |
146 SCEAffixPolicy.OPTIONAL, | |
147 custom_policies={ custom_affix: SCEAffixPolicy.OPTIONAL } | |
148 ) | |
149 | |
150 stanza = string_to_domish("""<message from="foo@example.com" to="bar@example.com"> | |
151 <body> | |
152 Make sure that both the REQUIRED and the OPTIONAL affixes are included. | |
153 </body> | |
154 </message>""") | |
155 | |
156 affix_values = XEP_0420.unpack_stanza( | |
157 profile, | |
158 stanza, | |
159 XEP_0420.pack_stanza(profile, stanza) | |
160 ) | |
161 | |
162 assert affix_values.rpad is not None | |
163 assert affix_values.timestamp is not None | |
164 assert affix_values.recipient is None | |
165 assert affix_values.sender is not None | |
166 assert custom_affix in affix_values.custom | |
167 | |
168 | |
169 def test_all_affixes_verified() -> None: # pylint: disable=missing-function-docstring | |
170 packing_profile = SCEProfile( | |
171 SCEAffixPolicy.REQUIRED, | |
172 SCEAffixPolicy.REQUIRED, | |
173 SCEAffixPolicy.REQUIRED, | |
174 SCEAffixPolicy.REQUIRED, | |
175 custom_policies={} | |
176 ) | |
177 | |
178 unpacking_profile = SCEProfile( | |
179 SCEAffixPolicy.NOT_NEEDED, | |
180 SCEAffixPolicy.NOT_NEEDED, | |
181 SCEAffixPolicy.NOT_NEEDED, | |
182 SCEAffixPolicy.NOT_NEEDED, | |
183 custom_policies={} | |
184 ) | |
185 | |
186 stanza = string_to_domish("""<message from="foo@example.com" to="bar@example.com"> | |
187 <body> | |
188 When unpacking, all affixes are loaded, even those marked as NOT_NEEDED. | |
189 </body> | |
190 </message>""") | |
191 | |
192 envelope_serialized = XEP_0420.pack_stanza(packing_profile, stanza) | |
193 | |
194 affix_values = XEP_0420.unpack_stanza(unpacking_profile, stanza, envelope_serialized) | |
195 | |
196 assert affix_values.rpad is not None | |
197 assert affix_values.timestamp is not None | |
198 assert affix_values.recipient is not None | |
199 assert affix_values.sender is not None | |
200 | |
201 # When unpacking, all affixes are verified, even if they are NOT_NEEDED by the profile | |
202 stanza = string_to_domish( | |
203 """<message from="fooo@example.com" to="baz@example.com"></message>""" | |
204 ) | |
205 | |
206 with pytest.raises(AffixVerificationFailed): | |
207 XEP_0420.unpack_stanza(unpacking_profile, stanza, envelope_serialized) | |
208 | |
209 | |
210 def test_incomplete_affixes() -> None: # pylint: disable=missing-function-docstring | |
211 packing_profile = SCEProfile( | |
212 SCEAffixPolicy.NOT_NEEDED, | |
213 SCEAffixPolicy.NOT_NEEDED, | |
214 SCEAffixPolicy.NOT_NEEDED, | |
215 SCEAffixPolicy.NOT_NEEDED, | |
216 custom_policies={} | |
217 ) | |
218 | |
219 unpacking_profile = SCEProfile( | |
220 SCEAffixPolicy.REQUIRED, | |
221 SCEAffixPolicy.REQUIRED, | |
222 SCEAffixPolicy.REQUIRED, | |
223 SCEAffixPolicy.REQUIRED, | |
224 custom_policies={} | |
225 ) | |
226 | |
227 stanza = string_to_domish("""<message from="foo@example.com" to="bar@example.com"> | |
228 <body>Check that all affixes REQUIRED by the profile are present.</body> | |
229 </message>""") | |
230 | |
231 with pytest.raises(ProfileRequirementsNotMet): | |
232 XEP_0420.unpack_stanza( | |
233 unpacking_profile, | |
234 stanza, | |
235 XEP_0420.pack_stanza(packing_profile, stanza) | |
236 ) | |
237 | |
238 # Do the same but with a custom affix missing | |
239 custom_affix = CustomAffixImpl() | |
240 | |
241 packing_profile = SCEProfile( | |
242 SCEAffixPolicy.NOT_NEEDED, | |
243 SCEAffixPolicy.NOT_NEEDED, | |
244 SCEAffixPolicy.NOT_NEEDED, | |
245 SCEAffixPolicy.NOT_NEEDED, | |
246 custom_policies={ custom_affix: SCEAffixPolicy.NOT_NEEDED } | |
247 ) | |
248 | |
249 unpacking_profile = SCEProfile( | |
250 SCEAffixPolicy.NOT_NEEDED, | |
251 SCEAffixPolicy.NOT_NEEDED, | |
252 SCEAffixPolicy.NOT_NEEDED, | |
253 SCEAffixPolicy.NOT_NEEDED, | |
254 custom_policies={ custom_affix: SCEAffixPolicy.REQUIRED } | |
255 ) | |
256 | |
257 stanza = string_to_domish("""<message from="foo@example.com" to="bar@example.com"> | |
258 <body> | |
259 Check that all affixes REQUIRED by the profile are present, including custom | |
260 affixes. | |
261 </body> | |
262 </message>""") | |
263 | |
264 with pytest.raises(ProfileRequirementsNotMet): | |
265 XEP_0420.unpack_stanza( | |
266 unpacking_profile, | |
267 stanza, | |
268 XEP_0420.pack_stanza(packing_profile, stanza) | |
269 ) | |
270 | |
271 | |
272 def test_rpad_affix() -> None: # pylint: disable=missing-function-docstring | |
273 profile = SCEProfile( | |
274 SCEAffixPolicy.REQUIRED, | |
275 SCEAffixPolicy.NOT_NEEDED, | |
276 SCEAffixPolicy.NOT_NEEDED, | |
277 SCEAffixPolicy.NOT_NEEDED, | |
278 custom_policies={} | |
279 ) | |
280 | |
281 for _ in range(100): | |
282 stanza = string_to_domish("""<message from="foo@example.com" to="bar@example.com"> | |
283 <body>OK</body> | |
284 </message>""") | |
285 | |
286 affix_values = XEP_0420.unpack_stanza( | |
287 profile, | |
288 stanza, | |
289 XEP_0420.pack_stanza(profile, stanza) | |
290 ) | |
291 | |
292 # Test that the rpad exists and that the content elements are always padded to at | |
293 # least 53 characters | |
294 assert affix_values.rpad is not None | |
295 assert len(affix_values.rpad) >= 53 - len("<body xmlns='jabber:client'>OK</body>") | |
296 | |
297 | |
298 def test_time_affix() -> None: # pylint: disable=missing-function-docstring | |
299 profile = SCEProfile( | |
300 SCEAffixPolicy.NOT_NEEDED, | |
301 SCEAffixPolicy.REQUIRED, | |
302 SCEAffixPolicy.NOT_NEEDED, | |
303 SCEAffixPolicy.NOT_NEEDED, | |
304 custom_policies={} | |
305 ) | |
306 | |
307 stanza = string_to_domish( | |
308 """<message from="foo@example.com" to="bar@example.com"></message>""" | |
309 ) | |
310 | |
311 envelope_serialized = f"""<envelope xmlns="{NS_SCE}"> | |
312 <content> | |
313 <body xmlns="jabber:client"> | |
314 The time affix is only parsed and not otherwise verified. Not much to test | |
315 here. | |
316 </body> | |
317 </content> | |
318 <time stamp="1969-07-21T02:56:15Z"/> | |
319 </envelope>""".encode("utf-8") | |
320 | |
321 affix_values = XEP_0420.unpack_stanza(profile, stanza, envelope_serialized) | |
322 assert affix_values.timestamp == datetime(1969, 7, 21, 2, 56, 15, tzinfo=timezone.utc) | |
323 | |
324 | |
325 def test_to_affix() -> None: # pylint: disable=missing-function-docstring | |
326 profile = SCEProfile( | |
327 SCEAffixPolicy.NOT_NEEDED, | |
328 SCEAffixPolicy.NOT_NEEDED, | |
329 SCEAffixPolicy.REQUIRED, | |
330 SCEAffixPolicy.NOT_NEEDED, | |
331 custom_policies={} | |
332 ) | |
333 | |
334 stanza = string_to_domish("""<message from="foo@example.com" to="bar@example.com"> | |
335 <body>Check that the ``to`` affix is correctly added.</body> | |
336 </message>""") | |
337 | |
338 envelope_serialized = XEP_0420.pack_stanza(profile, stanza) | |
339 affix_values = XEP_0420.unpack_stanza(profile, stanza, envelope_serialized) | |
340 assert affix_values.recipient is not None | |
341 assert affix_values.recipient.userhost() == "bar@example.com" | |
342 | |
343 # Check that a mismatch in recipient bare JID causes an exception to be raised | |
344 stanza = string_to_domish( | |
345 """<message from="foo@example.com" to="baz@example.com"></message>""" | |
346 ) | |
347 | |
348 with pytest.raises(AffixVerificationFailed): | |
349 XEP_0420.unpack_stanza(profile, stanza, envelope_serialized) | |
350 | |
351 # Check that only the bare JID matters | |
352 stanza = string_to_domish( | |
353 """<message from="foo@example.com" to="bar@example.com/device"></message>""" | |
354 ) | |
355 | |
356 affix_values = XEP_0420.unpack_stanza(profile, stanza, envelope_serialized) | |
357 assert affix_values.recipient is not None | |
358 assert affix_values.recipient.userhost() == "bar@example.com" | |
359 | |
360 stanza = string_to_domish("""<message from="foo@example.com"> | |
361 <body> | |
362 Check that a missing "to" attribute on the stanza fails stanza packing. | |
363 </body> | |
364 </message>""") | |
365 | |
366 with pytest.raises(ValueError): | |
367 XEP_0420.pack_stanza(profile, stanza) | |
368 | |
369 | |
370 def test_from_affix() -> None: # pylint: disable=missing-function-docstring | |
371 profile = SCEProfile( | |
372 SCEAffixPolicy.NOT_NEEDED, | |
373 SCEAffixPolicy.NOT_NEEDED, | |
374 SCEAffixPolicy.NOT_NEEDED, | |
375 SCEAffixPolicy.REQUIRED, | |
376 custom_policies={} | |
377 ) | |
378 | |
379 stanza = string_to_domish("""<message from="foo@example.com" to="bar@example.com"> | |
380 <body>Check that the ``from`` affix is correctly added.</body> | |
381 </message>""") | |
382 | |
383 envelope_serialized = XEP_0420.pack_stanza(profile, stanza) | |
384 affix_values = XEP_0420.unpack_stanza(profile, stanza, envelope_serialized) | |
385 assert affix_values.sender is not None | |
386 assert affix_values.sender.userhost() == "foo@example.com" | |
387 | |
388 # Check that a mismatch in sender bare JID causes an exception to be raised | |
389 stanza = string_to_domish( | |
390 """<message from="fooo@example.com" to="bar@example.com"></message>""" | |
391 ) | |
392 | |
393 with pytest.raises(AffixVerificationFailed): | |
394 XEP_0420.unpack_stanza(profile, stanza, envelope_serialized) | |
395 | |
396 # Check that only the bare JID matters | |
397 stanza = string_to_domish( | |
398 """<message from="foo@example.com/device" to="bar@example.com"></message>""" | |
399 ) | |
400 | |
401 affix_values = XEP_0420.unpack_stanza(profile, stanza, envelope_serialized) | |
402 assert affix_values.sender is not None | |
403 assert affix_values.sender.userhost() == "foo@example.com" | |
404 | |
405 stanza = string_to_domish("""<message to="bar@example.com"> | |
406 <body> | |
407 Check that a missing "from" attribute on the stanza fails stanza packing. | |
408 </body> | |
409 </message>""") | |
410 | |
411 with pytest.raises(ValueError): | |
412 XEP_0420.pack_stanza(profile, stanza) | |
413 | |
414 | |
415 def test_custom_affixes() -> None: # pylint: disable=missing-function-docstring | |
416 custom_affix = CustomAffixImpl() | |
417 | |
418 packing_profile = SCEProfile( | |
419 SCEAffixPolicy.NOT_NEEDED, | |
420 SCEAffixPolicy.NOT_NEEDED, | |
421 SCEAffixPolicy.NOT_NEEDED, | |
422 SCEAffixPolicy.NOT_NEEDED, | |
423 custom_policies={ custom_affix: SCEAffixPolicy.REQUIRED } | |
424 ) | |
425 | |
426 unpacking_profile = SCEProfile( | |
427 SCEAffixPolicy.NOT_NEEDED, | |
428 SCEAffixPolicy.NOT_NEEDED, | |
429 SCEAffixPolicy.NOT_NEEDED, | |
430 SCEAffixPolicy.NOT_NEEDED, | |
431 custom_policies={} | |
432 ) | |
433 | |
434 stanza = string_to_domish("""<message from="foo@example.com" to="bar@example.com"> | |
435 <body> | |
436 If a custom affix is included in the envelope, but not excpected by the | |
437 recipient, the schema validation should fail. | |
438 </body> | |
439 </message>""") | |
440 | |
441 with pytest.raises(ValueError): | |
442 XEP_0420.unpack_stanza( | |
443 unpacking_profile, | |
444 stanza, | |
445 XEP_0420.pack_stanza(packing_profile, stanza) | |
446 ) | |
447 | |
448 profile = packing_profile | |
449 | |
450 stanza = string_to_domish("""<message from="foo@example.com/device0" | |
451 to="bar@example.com/Libervia.123"> | |
452 <body>The affix element should be returned as part of the affix values.</body> | |
453 </message>""") | |
454 | |
455 affix_values = XEP_0420.unpack_stanza( | |
456 profile, | |
457 stanza, | |
458 XEP_0420.pack_stanza(profile, stanza) | |
459 ) | |
460 | |
461 assert custom_affix in affix_values.custom | |
462 assert affix_values.custom[custom_affix].getAttribute("recipient") == \ | |
463 "bar@example.com/Libervia.123" | |
464 assert affix_values.custom[custom_affix].getAttribute("sender") == \ | |
465 "foo@example.com/device0" | |
466 | |
467 | |
468 def test_namespace_conversion() -> None: # pylint: disable=missing-function-docstring | |
469 profile = SCEProfile( | |
470 SCEAffixPolicy.NOT_NEEDED, | |
471 SCEAffixPolicy.NOT_NEEDED, | |
472 SCEAffixPolicy.NOT_NEEDED, | |
473 SCEAffixPolicy.NOT_NEEDED, | |
474 custom_policies={} | |
475 ) | |
476 | |
477 stanza = domish.Element((None, "message")) | |
478 stanza["from"] = "foo@example.com" | |
479 stanza["to"] = "bar@example.com" | |
480 stanza.addElement( | |
481 "body", | |
482 content=( | |
483 "This body element has namespace ``None``, which has to be replaced with" | |
484 " jabber:client." | |
485 ) | |
486 ) | |
487 | |
488 envelope_serialized = XEP_0420.pack_stanza(profile, stanza) | |
489 envelope = string_to_domish(envelope_serialized.decode("utf-8")) | |
490 content = next(cast(Iterator[domish.Element], envelope.elements(NS_SCE, "content"))) | |
491 | |
492 # The body should have been assigned ``jabber:client`` as its namespace | |
493 assert next( | |
494 cast(Iterator[domish.Element], content.elements("jabber:client", "body")), | |
495 None | |
496 ) is not None | |
497 | |
498 XEP_0420.unpack_stanza(profile, stanza, envelope_serialized) | |
499 | |
500 # The body should still have ``jabber:client`` after unpacking | |
501 assert next( | |
502 cast(Iterator[domish.Element], stanza.elements("jabber:client", "body")), | |
503 None | |
504 ) is not None | |
505 | |
506 | |
507 def test_non_encryptable_elements() -> None: # pylint: disable=missing-function-docstring | |
508 profile = SCEProfile( | |
509 SCEAffixPolicy.NOT_NEEDED, | |
510 SCEAffixPolicy.NOT_NEEDED, | |
511 SCEAffixPolicy.NOT_NEEDED, | |
512 SCEAffixPolicy.NOT_NEEDED, | |
513 custom_policies={} | |
514 ) | |
515 | |
516 stanza = string_to_domish("""<message from="foo@example.com" to="bar@example.com"> | |
517 <body>This stanza includes a store hint which must not be encrypted.</body> | |
518 <store xmlns="urn:xmpp:hints"/> | |
519 </message>""") | |
520 | |
521 envelope_serialized = XEP_0420.pack_stanza(profile, stanza) | |
522 envelope = string_to_domish(envelope_serialized.decode("utf-8")) | |
523 content = next(cast(Iterator[domish.Element], envelope.elements(NS_SCE, "content"))) | |
524 | |
525 # The store hint must not have been moved to the content element | |
526 assert next( | |
527 cast(Iterator[domish.Element], stanza.elements(NS_HINTS, "store")), | |
528 None | |
529 ) is not None | |
530 | |
531 assert next( | |
532 cast(Iterator[domish.Element], content.elements(NS_HINTS, "store")), | |
533 None | |
534 ) is None | |
535 | |
536 stanza = string_to_domish( | |
537 """<message from="foo@example.com" to="bar@example.com"></message>""" | |
538 ) | |
539 | |
540 envelope_serialized = f"""<envelope xmlns="{NS_SCE}"> | |
541 <content> | |
542 <body xmlns="jabber:client"> | |
543 The store hint must not be moved to the stanza. | |
544 </body> | |
545 <store xmlns="urn:xmpp:hints"/> | |
546 </content> | |
547 </envelope>""".encode("utf-8") | |
548 | |
549 XEP_0420.unpack_stanza(profile, stanza, envelope_serialized) | |
550 | |
551 assert next( | |
552 cast(Iterator[domish.Element], stanza.elements(NS_HINTS, "store")), | |
553 None | |
554 ) is None | |
555 | |
556 | |
557 def test_schema_validation() -> None: # pylint: disable=missing-function-docstring | |
558 profile = SCEProfile( | |
559 SCEAffixPolicy.NOT_NEEDED, | |
560 SCEAffixPolicy.NOT_NEEDED, | |
561 SCEAffixPolicy.NOT_NEEDED, | |
562 SCEAffixPolicy.NOT_NEEDED, | |
563 custom_policies={} | |
564 ) | |
565 | |
566 stanza = string_to_domish( | |
567 """<message from="foo@example.com" to="bar@example.com"></message>""" | |
568 ) | |
569 | |
570 envelope_serialized = f"""<envelope xmlns="{NS_SCE}"> | |
571 <content> | |
572 <body xmlns="jabber:client"> | |
573 An unknwon affix should cause a schema validation error. | |
574 </body> | |
575 <store xmlns="urn:xmpp:hints"/> | |
576 </content> | |
577 <unknown-affix unknown-attr="unknown"/> | |
578 </envelope>""".encode("utf-8") | |
579 | |
580 with pytest.raises(ValueError): | |
581 XEP_0420.unpack_stanza(profile, stanza, envelope_serialized) |