1#!/usr/bin/env python
2#
3# spyne - Copyright (C) Spyne contributors.
4#
5# This library is free software; you can redistribute it and/or
6# modify it under the terms of the GNU Lesser General Public
7# License as published by the Free Software Foundation; either
8# version 2.1 of the License, or (at your option) any later version.
9#
10# This library is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13# Lesser General Public License for more details.
14#
15# You should have received a copy of the GNU Lesser General Public
16# License along with this library; if not, write to the Free Software
17# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
18#
19
20from __future__ import unicode_literals
21
22import logging
23logger = logging.getLogger(__name__)
24
25import unittest
26
27import uuid
28import pytz
29import decimal
30from spyne.util import six
31from spyne.util.dictdoc import get_object_as_dict
32
33if not six.PY2:
34    long = int
35
36from datetime import datetime
37from datetime import date
38from datetime import time
39from datetime import timedelta
40
41import lxml.etree
42import lxml.html
43
44from lxml.builder import E
45
46from spyne import MethodContext
47from spyne.service import Service
48from spyne.server import ServerBase
49from spyne.application import Application
50from spyne.decorator import srpc, rpc
51from spyne.error import ValidationError
52from spyne.model.binary import binary_encoding_handlers, File
53from spyne.model.complex import ComplexModel
54from spyne.model.complex import Iterable
55from spyne.model.fault import Fault
56from spyne.protocol import ProtocolBase
57from spyne.model.binary import ByteArray
58from spyne.model.primitive import Decimal
59from spyne.model.primitive import Integer
60from spyne.model.primitive import String
61from spyne.model.primitive import DateTime
62from spyne.model.primitive import Mandatory
63from spyne.model.primitive import AnyXml
64from spyne.model.primitive import AnyHtml
65from spyne.model.primitive import AnyDict
66from spyne.model.primitive import Unicode
67from spyne.model.primitive import AnyUri
68from spyne.model.primitive import ImageUri
69from spyne.model.primitive import Double
70from spyne.model.primitive import Integer8
71from spyne.model.primitive import Time
72from spyne.model.primitive import Date
73from spyne.model.primitive import Duration
74from spyne.model.primitive import Boolean
75from spyne.model.primitive import Uuid
76from spyne.model.primitive import Point
77from spyne.model.primitive import Line
78from spyne.model.primitive import Polygon
79from spyne.model.primitive import MultiPoint
80from spyne.model.primitive import MultiLine
81from spyne.model.primitive import MultiPolygon
82
83
84def _unbyte(d):
85    if d is None:
86        return
87
88    for k, v in list(d.items()):
89        if isinstance(k, bytes):
90            del d[k]
91            d[k.decode('utf8')] = v
92
93        if isinstance(v, dict):
94            _unbyte(v)
95
96    for k, v in d.items():
97        if isinstance(v, (list, tuple)):
98            l = []
99            for sub in v:
100                if isinstance(sub, dict):
101                    l.append(_unbyte(sub))
102
103                elif isinstance(sub, bytes):
104                    l.append(sub.decode("utf8"))
105
106                else:
107                    l.append(sub)
108
109            d[k] = tuple(l)
110
111        elif isinstance(v, bytes):
112            try:
113                d[k] = v.decode('utf8')
114            except UnicodeDecodeError:
115                d[k] = v
116
117    return d
118
119
120def TDry(serializer, _DictDocumentChild, dumps_kwargs=None):
121    if not dumps_kwargs:
122        dumps_kwargs = {}
123
124    def _dry_me(services, d, ignore_wrappers=False, complex_as=dict,
125                    just_ctx=False, just_in_object=False, validator=None,
126                                                             polymorphic=False):
127
128        app = Application(services, 'tns',
129                in_protocol=_DictDocumentChild(
130                    ignore_wrappers=ignore_wrappers, complex_as=complex_as,
131                    polymorphic=polymorphic, validator=validator,
132                ),
133                out_protocol=_DictDocumentChild(
134                     ignore_wrappers=ignore_wrappers, complex_as=complex_as,
135                                                       polymorphic=polymorphic),
136            )
137
138        server = ServerBase(app)
139        initial_ctx = MethodContext(server, MethodContext.SERVER)
140        in_string = serializer.dumps(d, **dumps_kwargs)
141        if not isinstance(in_string, bytes):
142            in_string = in_string.encode('utf8')
143        initial_ctx.in_string = [in_string]
144
145        ctx, = server.generate_contexts(initial_ctx, in_string_charset='utf8')
146        if not just_ctx:
147            server.get_in_object(ctx)
148            if not just_in_object:
149                server.get_out_object(ctx)
150                server.get_out_string(ctx)
151
152        return ctx
153    return _dry_me
154
155def TDictDocumentTest(serializer, _DictDocumentChild, dumps_kwargs=None,
156                                          loads_kwargs=None, convert_dict=None):
157    if not dumps_kwargs:
158        dumps_kwargs = {}
159    if not loads_kwargs:
160        loads_kwargs = {}
161    _dry_me = TDry(serializer, _DictDocumentChild, dumps_kwargs)
162
163    if convert_dict is None:
164        convert_dict = lambda v: v
165
166    class Test(unittest.TestCase):
167        def dumps(self, o):
168            print("using", dumps_kwargs, "to dump", o)
169            return serializer.dumps(o, **dumps_kwargs)
170
171        def loads(self, o):
172            return _unbyte(serializer.loads(o, **loads_kwargs))
173
174        def test_complex_with_only_primitive_fields(self):
175            class SomeComplexModel(ComplexModel):
176                i = Integer
177                s = Unicode
178
179            class SomeService(Service):
180                @srpc(SomeComplexModel, _returns=SomeComplexModel)
181                def some_call(scm):
182                    return SomeComplexModel(i=5, s='5x')
183
184            ctx = _dry_me([SomeService], {"some_call":[]})
185
186            s = self.loads(b''.join(ctx.out_string))
187
188            s = s["some_callResponse"]["some_callResult"]["SomeComplexModel"]
189            assert s["i"] == 5
190            assert s["s"] in ("5x", b"5x")
191
192        def test_complex(self):
193            class CM(ComplexModel):
194                i = Integer
195                s = Unicode
196
197            class CCM(ComplexModel):
198                c = CM
199                i = Integer
200                s = Unicode
201
202            class SomeService(Service):
203                @srpc(CCM, _returns=CCM)
204                def some_call(ccm):
205                    return CCM(c=ccm.c, i=ccm.i, s=ccm.s)
206
207            ctx = _dry_me([SomeService], {"some_call":
208                    {"ccm": {"CCM":{
209                        "c":{"CM":{"i":3, "s": "3x"}},
210                        "i":4,
211                        "s": "4x",
212                    }}}
213                })
214
215            ret = self.loads(b''.join(ctx.out_string))
216            print(ret)
217
218            d = ret['some_callResponse']['some_callResult']['CCM']
219            assert d['i'] == 4
220            assert d['s'] in ('4x', b'4x')
221            assert d['c']['CM']['i'] == 3
222            assert d['c']['CM']['s'] in ('3x', b'3x')
223
224        def test_multiple_list(self):
225            class SomeService(Service):
226                @srpc(Unicode(max_occurs=decimal.Decimal('inf')),
227                            _returns=Unicode(max_occurs=decimal.Decimal('inf')))
228                def some_call(s):
229                    return s
230
231            ctx = _dry_me([SomeService], {"some_call":[["a","b"]]})
232
233            data = b''.join(ctx.out_string)
234            print(data)
235
236            assert self.loads(data) == \
237                          {"some_callResponse": {"some_callResult": ("a", "b")}}
238
239        def test_multiple_dict(self):
240            class SomeService(Service):
241                @srpc(Unicode(max_occurs=decimal.Decimal('inf')),
242                            _returns=Unicode(max_occurs=decimal.Decimal('inf')))
243                def some_call(s):
244                    return s
245
246            ctx = _dry_me([SomeService], {"some_call":{"s":["a","b"]}})
247
248            assert self.loads(b''.join(ctx.out_string)) == \
249                    {"some_callResponse": {"some_callResult": ("a", "b")}}
250
251        def test_multiple_dict_array(self):
252            class SomeService(Service):
253                @srpc(Iterable(Unicode), _returns=Iterable(Unicode))
254                def some_call(s):
255                    return s
256
257            ctx = _dry_me([SomeService], {"some_call":{"s":["a","b"]}})
258
259            assert self.loads(b''.join(ctx.out_string)) == \
260                 {"some_callResponse": {"some_callResult": ("a", "b")}}
261
262        def test_multiple_dict_complex_array(self):
263            class CM(ComplexModel):
264                i = Integer
265                s = Unicode
266
267            class CCM(ComplexModel):
268                c = CM
269                i = Integer
270                s = Unicode
271
272            class ECM(CCM):
273                d = DateTime
274
275            class SomeService(Service):
276                @srpc(Iterable(ECM), _returns=Iterable(ECM))
277                def some_call(ecm):
278                    return ecm
279
280            ctx = _dry_me([SomeService], {
281                "some_call": {"ecm": [{"ECM": {
282                        "c": {"CM":{"i":3, "s": "3x"}},
283                        "i":4,
284                        "s": "4x",
285                        "d": "2011-12-13T14:15:16Z"
286                    }}]
287                }})
288
289            print(ctx.in_object)
290
291            ret = self.loads(b''.join(ctx.out_string))
292            print(ret)
293            assert ret["some_callResponse"]['some_callResult']
294            assert ret["some_callResponse"]['some_callResult'][0]
295            assert ret["some_callResponse"]['some_callResult'][0]["ECM"]["c"]
296            assert ret["some_callResponse"]['some_callResult'][0]["ECM"]["c"]["CM"]["i"] == 3
297            assert ret["some_callResponse"]['some_callResult'][0]["ECM"]["c"]["CM"]["s"] in ("3x", b"3x")
298            assert ret["some_callResponse"]['some_callResult'][0]["ECM"]["i"] == 4
299            assert ret["some_callResponse"]['some_callResult'][0]["ECM"]["s"] in ("4x", b"4x")
300            assert ret["some_callResponse"]['some_callResult'][0]["ECM"]["d"] == "2011-12-13T14:15:16+00:00"
301
302        def test_invalid_request(self):
303            class SomeService(Service):
304                @srpc(Integer, String, DateTime)
305                def yay(i,s,d):
306                    print(i,s,d)
307
308            ctx = _dry_me([SomeService], {"some_call": {"yay": []}},
309                                                            just_in_object=True)
310
311            print(ctx.in_error)
312            assert ctx.in_error.faultcode == 'Client.ResourceNotFound'
313
314        def test_invalid_string(self):
315            class SomeService(Service):
316                @srpc(Integer, String, DateTime)
317                def yay(i,s,d):
318                    print(i, s, d)
319
320            ctx = _dry_me([SomeService], {"yay": {"s": 1}}, validator='soft',
321                                                            just_in_object=True)
322
323            assert ctx.in_error.faultcode == 'Client.ValidationError'
324
325        def test_invalid_number(self):
326            class SomeService(Service):
327                @srpc(Integer, String, DateTime)
328                def yay(i,s,d):
329                    print(i,s,d)
330
331            ctx = _dry_me([SomeService], {"yay": ["s", "B"]}, validator='soft',
332                                                            just_in_object=True)
333
334            assert ctx.in_error.faultcode == 'Client.ValidationError'
335
336        def test_missing_value(self):
337            class SomeService(Service):
338                @srpc(Integer, Unicode, Mandatory.DateTime)
339                def yay(i, s, d):
340                    print(i, s, d)
341
342            ctx = _dry_me([SomeService], {"yay": [1, "B"]}, validator='soft',
343                                                            just_in_object=True)
344
345            print(ctx.in_error.faultstring)
346            assert ctx.in_error.faultcode == 'Client.ValidationError'
347            assert ctx.in_error.faultstring.endswith("at least 1 times.")
348
349        def test_invalid_datetime(self):
350            class SomeService(Service):
351                @srpc(Integer, String, Mandatory.DateTime)
352                def yay(i,s,d):
353                    print(i,s,d)
354
355            ctx = _dry_me([SomeService],{"yay": {"d":"a2011"}},validator='soft',
356                                                            just_in_object=True)
357
358            assert ctx.in_error.faultcode == 'Client.ValidationError'
359
360        def test_fault_to_dict(self):
361            class SomeService(Service):
362                @srpc(_returns=String)
363                def some_call():
364                    raise Fault()
365
366            _dry_me([SomeService], {"some_call":[]})
367
368        def test_prune_none_and_optional(self):
369            class SomeObject(ComplexModel):
370                i = Integer
371                s = String(min_occurs=1)
372
373            class SomeService(Service):
374                @srpc(_returns=SomeObject)
375                def some_call():
376                    return SomeObject()
377
378            ctx = _dry_me([SomeService], {"some_call":[]})
379
380            ret = self.loads(b''.join(ctx.out_string))
381
382            assert ret == {"some_callResponse": {'some_callResult':
383                                                   {'SomeObject': {'s': None}}}}
384
385        def test_any_xml(self):
386            d = lxml.etree.tostring(E('{ns1}x', E('{ns2}Y', "some data")),
387                                                             encoding='unicode')
388
389            class SomeService(Service):
390                @srpc(AnyXml, _returns=AnyXml)
391                def some_call(p):
392                    print(p)
393                    print(type(p))
394                    assert type(p) == lxml.etree._Element
395                    return p
396
397            ctx = _dry_me([SomeService], {"some_call":[d]})
398
399            s = self.loads(b''.join(ctx.out_string))
400            d = {"some_callResponse": {"some_callResult": d}}
401            print(s)
402            print(d)
403            assert s == d
404
405        def test_any_html(self):
406            d = lxml.html.tostring(E('div', E('span', "something")),
407                                                             encoding='unicode')
408
409            class SomeService(Service):
410                @srpc(AnyHtml, _returns=AnyHtml)
411                def some_call(p):
412                    print(p)
413                    print(type(p))
414                    assert type(p) == lxml.html.HtmlElement
415                    return p
416
417            ctx = _dry_me([SomeService], {"some_call": [d]})
418
419            s = self.loads(b''.join(ctx.out_string))
420            d = {"some_callResponse": {"some_callResult": d}}
421
422            print(s)
423            print(d)
424            assert s == d
425
426        def test_any_dict(self):
427            d = {'helo': 213, 'data': {'nested': [12, 0.3]}}
428
429            class SomeService(Service):
430                @srpc(AnyDict, _returns=AnyDict)
431                def some_call(p):
432                    print(p)
433                    print(type(p))
434                    assert type(p) == dict
435                    return p
436
437            ctx = _dry_me([SomeService], {"some_call":[d]})
438
439            s = b''.join(ctx.out_string)
440            d = self.dumps({"some_callResponse": {"some_callResult": d}})
441
442            print(s)
443            print(d)
444            assert self.loads(s) == self.loads(d)
445
446        def test_unicode(self):
447            d = u'some string'
448
449            class SomeService(Service):
450                @srpc(Unicode, _returns=Unicode)
451                def some_call(p):
452                    print(p)
453                    print(type(p))
454                    assert type(p) == six.text_type
455                    return p
456
457            ctx = _dry_me([SomeService], {"some_call":[d]})
458
459            s = self.loads(b''.join(ctx.out_string))
460            d = {"some_callResponse": {"some_callResult": d}}
461            print(s)
462            print(d)
463            assert s == d
464
465        def test_any_uri(self):
466            d = 'http://example.com/?asd=b12&df=aa#tag'
467
468            class SomeService(Service):
469                @srpc(AnyUri, _returns=AnyUri)
470                def some_call(p):
471                    print(p)
472                    print(type(p))
473                    assert isinstance(p, six.string_types)
474                    return p
475
476            ctx = _dry_me([SomeService], {"some_call": [d]})
477
478            s = self.loads(b''.join(ctx.out_string))
479            d = {"some_callResponse": {"some_callResult": d}}
480            print(s)
481            print(d)
482            assert s == d
483
484        def test_image_uri(self):
485            d = 'http://example.com/funny.gif'
486
487            class SomeService(Service):
488                @srpc(ImageUri, _returns=ImageUri)
489                def some_call(p):
490                    print(p)
491                    print(type(p))
492                    assert isinstance(p, six.string_types)
493                    return p
494
495            ctx = _dry_me([SomeService], {"some_call": [d]})
496
497            s = self.loads(b''.join(ctx.out_string))
498            d = {"some_callResponse": {"some_callResult": d}}
499            print(s)
500            print(d)
501            assert s == d
502
503        def test_decimal(self):
504            d = decimal.Decimal('1e100')
505            if _DictDocumentChild._decimal_as_string:
506                d = str(d)
507
508            class SomeService(Service):
509                @srpc(Decimal, _returns=Decimal)
510                def some_call(p):
511                    print(p)
512                    print(type(p))
513                    assert type(p) == decimal.Decimal
514                    return p
515
516            ctx = _dry_me([SomeService], {"some_call": [d]})
517
518            s = self.loads(b''.join(ctx.out_string))
519            d = {"some_callResponse": {"some_callResult": d}}
520            print(s)
521            print(d)
522            assert s == d
523
524        def test_double(self):
525            d = 12.3467
526
527            class SomeService(Service):
528                @srpc(Double, _returns=Double)
529                def some_call(p):
530                    print(p)
531                    print(type(p))
532                    assert type(p) == float
533                    return p
534
535            ctx = _dry_me([SomeService], {"some_call":[d]})
536
537            s = self.loads(b''.join(ctx.out_string))
538            d = {"some_callResponse": {"some_callResult": d}}
539            print(s)
540            print(d)
541            assert s == d
542
543        def test_integer(self):
544            d = 5
545
546            class SomeService(Service):
547                @srpc(Integer, _returns=Integer)
548                def some_call(p):
549                    print(p)
550                    print(type(p))
551                    assert type(p) == int
552                    return p
553
554            ctx = _dry_me([SomeService], {"some_call":[d]})
555
556            s = self.loads(b''.join(ctx.out_string))
557            d = {"some_callResponse": {"some_callResult": d}}
558            print(s)
559            print(d)
560            assert s == d
561
562        def test_integer_way_small(self):
563            d = -1<<1000
564            if _DictDocumentChild._huge_numbers_as_string:
565                d = str(d)
566
567            class SomeService(Service):
568                @srpc(Integer, _returns=Integer)
569                def some_call(p):
570                    print(p)
571                    print(type(p))
572                    assert type(p) == long
573                    return p
574
575            ctx = _dry_me([SomeService], {"some_call":[d]})
576
577            s = self.loads(b''.join(ctx.out_string))
578            d = {"some_callResponse": {"some_callResult": d}}
579
580            print(s)
581            print(d)
582            assert s == d
583
584        def test_integer_way_big(self):
585            d = 1<<1000
586            if _DictDocumentChild._huge_numbers_as_string:
587                d = str(d)
588
589            class SomeService(Service):
590                @srpc(Integer, _returns=Integer)
591                def some_call(p):
592                    print(p)
593                    print(type(p))
594                    assert type(p) == long
595                    return p
596
597            ctx = _dry_me([SomeService], {"some_call":[d]})
598
599            s = self.loads(b''.join(ctx.out_string))
600            d = {"some_callResponse": {"some_callResult": d}}
601            print(s)
602            print(d)
603            assert s == d
604
605        def test_time(self):
606            d = time(10, 20, 30).isoformat()
607
608            class SomeService(Service):
609                @srpc(Time, _returns=Time)
610                def some_call(p):
611                    print(p)
612                    print(type(p))
613                    assert type(p) == time
614                    assert p.isoformat() == d
615                    return p
616
617            ctx = _dry_me([SomeService], {"some_call":[d]})
618
619            s = self.loads(b''.join(ctx.out_string))
620            d = {"some_callResponse": {"some_callResult": d}}
621            print(s)
622            print(d)
623            assert s == d
624
625        def test_date(self):
626            vdt = datetime(2010, 9, 8)
627            d = vdt.date().isoformat()
628
629            class SomeService(Service):
630                @srpc(Date, _returns=Date)
631                def some_call(p):
632                    print(p)
633                    print(type(p))
634                    assert type(p) == date
635                    assert p.isoformat() == d
636                    return p
637
638                @srpc(_returns=Date)
639                def some_call_dt():
640                    return vdt
641
642            ctx = _dry_me([SomeService], {"some_call": [d]})
643            s = self.loads(b''.join(ctx.out_string))
644            rd = {"some_callResponse": {"some_callResult": d}}
645            print(s)
646            print(rd)
647            assert s == rd
648
649            ctx = _dry_me([SomeService], {"some_call_dt": []})
650            s = self.loads(b''.join(ctx.out_string))
651            rd = {"some_call_dtResponse": {"some_call_dtResult": d}}
652            print(s)
653            print(rd)
654            assert s == rd
655
656        def test_datetime(self):
657            d = datetime(2010, 9, 8, 7, 6, 5).isoformat()
658
659            class SomeService(Service):
660                @srpc(DateTime, _returns=DateTime(timezone=False))
661                def some_call(p):
662                    print(p)
663                    print(type(p))
664                    assert type(p) == datetime
665                    assert p.replace(tzinfo=None).isoformat() == d
666                    return p
667
668            ctx = _dry_me([SomeService], {"some_call":[d]}, validator='soft')
669
670            s = self.loads(b''.join(ctx.out_string))
671            d = {"some_callResponse": {"some_callResult": d}}
672            print(s)
673            print(d)
674            assert s == d
675
676        def test_datetime_tz(self):
677            d = datetime(2010, 9, 8, 7, 6, 5, tzinfo=pytz.utc).isoformat()
678
679            class SomeService(Service):
680                @srpc(DateTime, _returns=DateTime(ge=datetime(2010,1,1,tzinfo=pytz.utc)))
681                def some_call(p):
682                    print(p)
683                    print(type(p))
684                    assert type(p) == datetime
685                    assert p.isoformat() == d
686                    return p
687
688            ctx = _dry_me([SomeService], {"some_call":[d]}, validator='soft')
689
690            s = self.loads(b''.join(ctx.out_string))
691            d = {"some_callResponse": {"some_callResult": d}}
692            print(s)
693            print(d)
694            assert s == d
695
696        def test_duration(self):
697            d = ProtocolBase().to_unicode(Duration, timedelta(0, 45))
698
699            class SomeService(Service):
700                @srpc(Duration, _returns=Duration)
701                def some_call(p):
702                    print(p)
703                    print(type(p))
704                    assert type(p) == timedelta
705                    return p
706
707            ctx = _dry_me([SomeService], {"some_call":[d]})
708
709            s = self.loads(b''.join(ctx.out_string))
710            d = {"some_callResponse": {"some_callResult": d}}
711            print(s)
712            print(d)
713            assert s == d
714
715        def test_boolean(self):
716            d = True
717
718            class SomeService(Service):
719                @srpc(Boolean, _returns=Boolean)
720                def some_call(p):
721                    print(p)
722                    print(type(p))
723                    assert type(p) == bool
724                    return p
725
726            ctx = _dry_me([SomeService], {"some_call":[d]})
727
728            s = self.loads(b''.join(ctx.out_string))
729            d = {"some_callResponse": {"some_callResult": d}}
730            print(s)
731            print(d)
732            assert s == d
733
734        def test_uuid(self):
735            d = '7d2a6330-eb64-4900-8a10-38ebef415e9d'
736
737            class SomeService(Service):
738                @srpc(Uuid, _returns=Uuid)
739                def some_call(p):
740                    print(p)
741                    print(type(p))
742                    assert type(p) == uuid.UUID
743                    return p
744
745            ctx = _dry_me([SomeService], {"some_call":[d]})
746
747            s = self.loads(b''.join(ctx.out_string))
748            d = {"some_callResponse": {"some_callResult": d}}
749            print(s)
750            print(d)
751            assert s == d
752
753        def test_point2d(self):
754            d = 'POINT(1 2)'
755
756            class SomeService(Service):
757                @srpc(Point, _returns=Point)
758                def some_call(p):
759                    print(p)
760                    print(type(p))
761                    assert isinstance(p, six.string_types)
762                    return p
763
764            ctx = _dry_me([SomeService], {"some_call":[d]})
765
766            s = self.loads(b''.join(ctx.out_string))
767            d = {"some_callResponse": {"some_callResult": d}}
768            print(s)
769            print(d)
770            assert s == d
771
772        def test_point3d(self):
773            d = 'POINT(1 2 3)'
774
775            class SomeService(Service):
776                @srpc(Point, _returns=Point)
777                def some_call(p):
778                    print(p)
779                    print(type(p))
780                    assert isinstance(p, six.string_types)
781                    return p
782
783            ctx = _dry_me([SomeService], {"some_call":[d]})
784
785            s = self.loads(b''.join(ctx.out_string))
786            d = {"some_callResponse": {"some_callResult": d}}
787            print(s)
788            print(d)
789            assert s == d
790
791        def test_line2d(self):
792            d = 'LINESTRING(1 2, 3 4)'
793
794            class SomeService(Service):
795                @srpc(Line, _returns=Line)
796                def some_call(p):
797                    print(p)
798                    print(type(p))
799                    assert isinstance(p, six.string_types)
800                    return p
801
802            ctx = _dry_me([SomeService], {"some_call":[d]})
803
804            s = self.loads(b''.join(ctx.out_string))
805            d = {"some_callResponse": {"some_callResult": d}}
806            print(s)
807            print(d)
808            assert s == d
809
810        def test_line3d(self):
811            d = 'LINESTRING(1 2 3, 4 5 6)'
812
813            class SomeService(Service):
814                @srpc(Line, _returns=Line)
815                def some_call(p):
816                    print(p)
817                    print(type(p))
818                    assert isinstance(p, six.string_types)
819                    return p
820
821            ctx = _dry_me([SomeService], {"some_call":[d]})
822
823            s = self.loads(b''.join(ctx.out_string))
824            d = {"some_callResponse": {"some_callResult": d}}
825            print(s)
826            print(d)
827            assert s == d
828
829        def test_polygon2d(self):
830            d = 'POLYGON((1 1, 1 2, 2 2, 2 1, 1 1))'
831
832            class SomeService(Service):
833                @srpc(Polygon(2), _returns=Polygon(2))
834                def some_call(p):
835                    print(p)
836                    print(type(p))
837                    assert isinstance(p, six.string_types)
838                    return p
839
840            ctx = _dry_me([SomeService], {"some_call":[d]})
841
842            s = self.loads(b''.join(ctx.out_string))
843            d = {"some_callResponse": {"some_callResult": d}}
844            print(s)
845            print(d)
846            assert s == d
847
848        def test_polygon3d(self):
849            d = 'POLYGON((1 1 0, 1 2 0, 2 2 0, 2 1 0, 1 1 0))'
850
851            class SomeService(Service):
852                @srpc(Polygon(3), _returns=Polygon(3))
853                def some_call(p):
854                    print(p)
855                    print(type(p))
856                    assert isinstance(p, six.string_types)
857                    return p
858
859            ctx = _dry_me([SomeService], {"some_call":[d]})
860
861            s = self.loads(b''.join(ctx.out_string))
862            d = {"some_callResponse": {"some_callResult": d}}
863            print(s)
864            print(d)
865            assert s == d
866
867        def test_multipoint2d(self):
868            d = 'MULTIPOINT ((10 40), (40 30), (20 20), (30 10))'
869
870            class SomeService(Service):
871                @srpc(MultiPoint(2), _returns=MultiPoint(2))
872                def some_call(p):
873                    print(p)
874                    print(type(p))
875                    assert isinstance(p, six.string_types)
876                    return p
877
878            ctx = _dry_me([SomeService], {"some_call":[d]})
879
880            s = self.loads(b''.join(ctx.out_string))
881            d = {"some_callResponse": {"some_callResult": d}}
882            print(s)
883            print(d)
884            assert s == d
885
886        def test_multipoint3d(self):
887            d = 'MULTIPOINT (10 40 30, 40 30 10,)'
888
889            class SomeService(Service):
890                @srpc(MultiPoint(3), _returns=MultiPoint(3))
891                def some_call(p):
892                    print(p)
893                    print(type(p))
894                    assert isinstance(p, six.string_types)
895                    return p
896
897            ctx = _dry_me([SomeService], {"some_call":[d]})
898
899            s = self.loads(b''.join(ctx.out_string))
900            d = {"some_callResponse": {"some_callResult": d}}
901            print(s)
902            print(d)
903            assert s == d
904
905        def test_multiline2d(self):
906            d = 'MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10))'
907
908            class SomeService(Service):
909                @srpc(MultiLine(2), _returns=MultiLine(2))
910                def some_call(p):
911                    print(p)
912                    print(type(p))
913                    assert isinstance(p, six.string_types)
914                    return p
915
916            ctx = _dry_me([SomeService], {"some_call":[d]})
917
918            s = self.loads(b''.join(ctx.out_string))
919            d = {"some_callResponse": {"some_callResult": d}}
920            print(s)
921            print(d)
922            assert s == d
923
924        def test_multiline3d(self):
925            d = 'MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10))'
926
927            class SomeService(Service):
928                @srpc(MultiLine(3), _returns=MultiLine(3))
929                def some_call(p):
930                    print(p)
931                    print(type(p))
932                    assert isinstance(p, six.string_types)
933                    return p
934
935            ctx = _dry_me([SomeService], {"some_call":[d]})
936
937            s = self.loads(b''.join(ctx.out_string))
938            d = {"some_callResponse": {"some_callResult": d}}
939            print(s)
940            print(d)
941            assert s == d
942
943        def test_multipolygon2d(self):
944            d = 'MULTIPOLYGON (((30 20, 10 40, 45 40, 30 20)),((15 5, 40 10, 10 20, 5 10, 15 5)))'
945
946            class SomeService(Service):
947                @srpc(MultiPolygon(2), _returns=MultiPolygon(2))
948                def some_call(p):
949                    print(p)
950                    print(type(p))
951                    assert isinstance(p, six.string_types)
952                    return p
953
954            ctx = _dry_me([SomeService], {"some_call":[d]})
955
956            s = self.loads(b''.join(ctx.out_string))
957            d = {"some_callResponse": {"some_callResult": d}}
958            print(s)
959            print(d)
960            assert s == d
961
962        def test_multipolygon3d(self):
963            d = 'MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),' \
964                              '((20 35, 45 20, 30 5, 10 10, 10 30, 20 35),' \
965                               '(30 20, 20 25, 20 15, 30 20)))'
966
967            class SomeService(Service):
968                @srpc(MultiPolygon(3), _returns=MultiPolygon(3))
969                def some_call(p):
970                    print(p)
971                    print(type(p))
972                    assert isinstance(p, six.string_types)
973                    return p
974
975            ctx = _dry_me([SomeService], {"some_call":[d]})
976
977            s = self.loads(b''.join(ctx.out_string))
978            d = {"some_callResponse": {"some_callResult": d}}
979            print(s)
980            print(d)
981            assert s == d
982
983        def test_generator(self):
984            class SomeService(Service):
985                @srpc(_returns=Iterable(Integer))
986                def some_call():
987                    return iter(range(1000))
988
989            ctx = _dry_me([SomeService], {"some_call":[]})
990
991            s = self.loads(b''.join(ctx.out_string))
992            d = {"some_callResponse": {"some_callResult": tuple(range(1000))}}
993            print(s)
994            print(d)
995            assert s == d
996
997        def test_bytearray(self):
998            dbe = _DictDocumentChild.default_binary_encoding
999            beh = binary_encoding_handlers[dbe]
1000
1001            data = bytes(bytearray(range(0xff)))
1002            encoded_data = beh([data])
1003            if _DictDocumentChild.text_based:
1004                encoded_data = encoded_data.decode('latin1')
1005
1006            class SomeService(Service):
1007                @srpc(ByteArray, _returns=ByteArray)
1008                def some_call(ba):
1009                    print(ba)
1010                    print(type(ba))
1011                    assert isinstance(ba, tuple)
1012                    assert ba == (data,)
1013                    return ba
1014
1015            ctx = _dry_me([SomeService], {"some_call": [encoded_data]})
1016
1017            s = self.loads(b''.join(ctx.out_string))
1018            d = {"some_callResponse": {"some_callResult": encoded_data}}
1019
1020            print(repr(s))
1021            print(repr(d))
1022            print(repr(encoded_data))
1023            assert s == d
1024
1025        def test_file_data(self):
1026            # the only difference with the bytearray test is/are the types
1027            # inside @srpc
1028            dbe = _DictDocumentChild.default_binary_encoding
1029            beh = binary_encoding_handlers[dbe]
1030
1031            data = bytes(bytearray(range(0xff)))
1032            encoded_data = beh([data])
1033            if _DictDocumentChild.text_based:
1034                encoded_data = encoded_data.decode('latin1')
1035
1036            class SomeService(Service):
1037                @srpc(File, _returns=File)
1038                def some_call(p):
1039                    print(p)
1040                    print(type(p))
1041                    assert isinstance(p, File.Value)
1042                    assert p.data == (data,)
1043                    return p.data
1044
1045            # we put the encoded data in the list of arguments.
1046            ctx = _dry_me([SomeService], {"some_call": [encoded_data]})
1047
1048            s = self.loads(b''.join(ctx.out_string))
1049            d = {"some_callResponse": {"some_callResult": encoded_data}}
1050
1051            print(s)
1052            print(d)
1053            print(repr(encoded_data))
1054            assert s == d
1055
1056        def test_file_value(self):
1057            dbe = _DictDocumentChild.default_binary_encoding
1058            beh = binary_encoding_handlers[dbe]
1059
1060            # Prepare data
1061            v = File.Value(
1062                name='some_file.bin',
1063                type='application/octet-stream',
1064            )
1065            file_data = bytes(bytearray(range(0xff)))
1066            v.data = (file_data,)
1067            beh([file_data])
1068            if _DictDocumentChild.text_based:
1069                test_data = beh(v.data).decode('latin1')
1070            else:
1071                test_data = beh(v.data)
1072
1073            print(repr(v.data))
1074
1075            class SomeService(Service):
1076                @srpc(File, _returns=File)
1077                def some_call(p):
1078                    print(p)
1079                    print(type(p))
1080                    assert isinstance(p, File.Value)
1081                    assert p.data == (file_data,)
1082                    assert p.type == v.type
1083                    assert p.name == v.name
1084                    return p
1085
1086            d = get_object_as_dict(v, File, protocol=_DictDocumentChild,
1087                                                          ignore_wrappers=False)
1088            ctx = _dry_me([SomeService], {"some_call": {'p': d}})
1089            s = b''.join(ctx.out_string)
1090            d = self.dumps({"some_callResponse": {"some_callResult": {
1091                'name': v.name,
1092                'type': v.type,
1093                'data': test_data,
1094            }}})
1095
1096            print(self.loads(s))
1097            print(self.loads(d))
1098            print(v)
1099            assert self.loads(s) == self.loads(d)
1100
1101        def test_validation_frequency(self):
1102            class SomeService(Service):
1103                @srpc(ByteArray(min_occurs=1), _returns=ByteArray)
1104                def some_call(p):
1105                    pass
1106
1107            try:
1108                _dry_me([SomeService], {"some_call": []}, validator='soft')
1109            except ValidationError:
1110                pass
1111            else:
1112                raise Exception("must raise ValidationError")
1113
1114        def test_validation_nullable(self):
1115            class SomeService(Service):
1116                @srpc(ByteArray(nullable=False), _returns=ByteArray)
1117                def some_call(p):
1118                    pass
1119
1120            try:
1121                _dry_me([SomeService], {"some_call": [None]},
1122                                                            validator='soft')
1123            except ValidationError:
1124                pass
1125
1126            else:
1127                raise Exception("must raise ValidationError")
1128
1129        def test_validation_string_pattern(self):
1130            class SomeService(Service):
1131                @srpc(Uuid)
1132                def some_call(p):
1133                    pass
1134
1135            try:
1136                _dry_me([SomeService], {"some_call": ["duduk"]},
1137                                                               validator='soft')
1138            except ValidationError as e:
1139                print(e)
1140                pass
1141
1142            else:
1143                raise Exception("must raise ValidationError")
1144
1145        def test_validation_integer_range(self):
1146            class SomeService(Service):
1147                @srpc(Integer(ge=0, le=5))
1148                def some_call(p):
1149                    pass
1150
1151            try:
1152                _dry_me([SomeService], {"some_call": [10]},
1153                                                            validator='soft')
1154            except ValidationError:
1155                pass
1156
1157            else:
1158                raise Exception("must raise ValidationError")
1159
1160        def test_validation_integer_type(self):
1161            class SomeService(Service):
1162                @srpc(Integer8)
1163                def some_call(p):
1164                    pass
1165
1166            try:
1167                _dry_me([SomeService], {"some_call": [-129]},
1168                                                            validator='soft')
1169            except ValidationError:
1170                pass
1171
1172            else:
1173                raise Exception("must raise ValidationError")
1174
1175        def test_validation_integer_type_2(self):
1176            class SomeService(Service):
1177                @srpc(Integer8)
1178                def some_call(p):
1179                    pass
1180
1181            try:
1182                _dry_me([SomeService], {"some_call": [1.2]}, validator='soft')
1183
1184            except ValidationError:
1185                pass
1186
1187            else:
1188                raise Exception("must raise ValidationError")
1189
1190        def test_not_wrapped(self):
1191            class SomeInnerClass(ComplexModel):
1192                d = date
1193                dt = datetime
1194
1195            class SomeClass(ComplexModel):
1196                a = int
1197                b = Unicode
1198                c = SomeInnerClass.customize(not_wrapped=True)
1199
1200            class SomeService(Service):
1201                @srpc(SomeClass.customize(not_wrapped=True),
1202                      _returns=SomeClass.customize(not_wrapped=True))
1203                def some_call(p):
1204                    assert p.a == 1
1205                    assert p.b == 's'
1206                    assert p.c.d == date(2018, 11, 22)
1207                    return p
1208
1209            inner = {"a": 1, "b": "s", "c": {"d": '2018-11-22'}}
1210            doc = {"some_call": [inner]}
1211            ctx = _dry_me([SomeService], doc, validator='soft')
1212
1213            print(ctx.out_document)
1214
1215            d = convert_dict({"some_callResponse": {"some_callResult": inner}})
1216            self.assertEquals(ctx.out_document[0], d)
1217
1218        def test_validation_freq_parent(self):
1219            class C(ComplexModel):
1220                i = Integer(min_occurs=1)
1221                s = Unicode
1222
1223            class SomeService(Service):
1224                @srpc(C)
1225                def some_call(p):
1226                    pass
1227
1228            try:
1229                # must raise validation error for missing i
1230                _dry_me([SomeService], {"some_call": {'p': {'C': {'s': 'a'}}}},
1231                                                               validator='soft')
1232            except ValidationError as e:
1233                logger.exception(e)
1234                pass
1235            except BaseException as e:
1236                logger.exception(e)
1237                pass
1238            else:
1239                raise Exception("must raise ValidationError")
1240
1241            # must not raise anything for missing p because C has min_occurs=0
1242            _dry_me([SomeService], {"some_call": {}}, validator='soft')
1243
1244        def test_inheritance(self):
1245            class P(ComplexModel):
1246                identifier = Uuid
1247                signature = Unicode
1248
1249            class C(P):
1250                foo = Unicode
1251                bar = Uuid
1252
1253            class SomeService(Service):
1254                @rpc(_returns=C)
1255                def some_call(ctx):
1256                    result = C()
1257                    result.identifier = uuid.UUID(int=0)
1258                    result.signature = 'yyyyyyyyyyy'
1259                    result.foo = 'zzzzzz'
1260                    result.bar = uuid.UUID(int=1)
1261                    return result
1262
1263            ctx = _dry_me([SomeService], {"some_call": []})
1264
1265            s = self.loads(b''.join(ctx.out_string))
1266            d = {"some_callResponse": {"some_callResult": {"C": {
1267                    'identifier': '00000000-0000-0000-0000-000000000000',
1268                    'bar': '00000000-0000-0000-0000-000000000001',
1269                    'foo': 'zzzzzz',
1270                    'signature': 'yyyyyyyyyyy'
1271                }}}}
1272
1273            assert s == d
1274
1275        def test_exclude(self):
1276            class C(ComplexModel):
1277                s1 = Unicode(exc=True)
1278                s2 = Unicode
1279
1280            class SomeService(Service):
1281                @srpc(C, _returns=C)
1282                def some_call(sc):
1283                    assert sc.s1 is None, "sc={}".format(sc)
1284                    assert sc.s2 == "s2"
1285                    return C(s1="s1", s2="s2")
1286
1287            doc = [{"C": {"s1": "s1","s2": "s2"}}]
1288            ctx = _dry_me([SomeService], {"some_call": doc})
1289
1290            self.assertEquals(ctx.out_document[0], convert_dict(
1291                {'some_callResponse': {'some_callResult': {'C': {'s2': 's2'}}}})
1292            )
1293
1294        def test_polymorphic_deserialization(self):
1295            class P(ComplexModel):
1296                sig = Unicode
1297
1298            class C(P):
1299                foo = Unicode
1300
1301            class D(P):
1302                bar = Integer
1303
1304            class SomeService(Service):
1305                @rpc(P, _returns=Unicode)
1306                def typeof(ctx, p):
1307                    return type(p).__name__
1308
1309            ctx = _dry_me([SomeService],
1310                            {"typeof": [{'C':{'sig':'a', 'foo': 'f'}}]},
1311                                                               polymorphic=True)
1312
1313            s = self.loads(b''.join(ctx.out_string))
1314            d = {"typeofResponse": {"typeofResult": 'C'}}
1315
1316            print(s)
1317            print(d)
1318            assert s == d
1319
1320            ctx = _dry_me([SomeService],
1321                                  {"typeof": [{'D':{'sig':'b', 'bar': 5}}]},
1322                                                               polymorphic=True)
1323
1324            s = self.loads(b''.join(ctx.out_string))
1325            d = {"typeofResponse": {"typeofResult": 'D'}}
1326
1327            print(s)
1328            print(d)
1329            assert s == d
1330
1331        def test_default(self):
1332            class SomeComplexModel(ComplexModel):
1333                _type_info = [
1334                    ('a', Unicode),
1335                    ('b', Unicode(default='default')),
1336                ]
1337
1338            class SomeService(Service):
1339                @srpc(SomeComplexModel)
1340                def some_method(s):
1341                    pass
1342
1343            ctx = _dry_me([SomeService],
1344                            {"some_method": [{"s": {"a": "x", "b": None}}]},
1345                                                               polymorphic=True)
1346
1347            assert ctx.in_object.s.b == None
1348            assert ctx.in_error is None
1349
1350            ctx = _dry_me([SomeService], {"some_method": {"s": {"a": "x"}}},
1351                                                               polymorphic=True)
1352
1353            assert ctx.in_object.s.b == 'default'
1354            assert ctx.in_error is None
1355
1356        def test_nillable_default(self):
1357            class SomeComplexModel(ComplexModel):
1358                _type_info = [
1359                    ('a', Unicode),
1360                    ('b', Unicode(min_occurs=1, default='default', nillable=True)),
1361                ]
1362
1363            class SomeService(Service):
1364                @srpc(SomeComplexModel)
1365                def some_method(s):
1366                    pass
1367
1368            ctx = _dry_me([SomeService],
1369                            {"some_method": [{"s": {"a": "x", "b": None}}]},
1370                                           polymorphic=True, validator='soft')
1371
1372            assert ctx.in_object.s.b == None
1373            assert ctx.in_error is None
1374
1375            ctx = _dry_me([SomeService], {"some_method": {"s": {"a": "x"}}},
1376                                                               polymorphic=True)
1377
1378            assert ctx.in_object.s.b == 'default'
1379            assert ctx.in_error is None
1380
1381    return Test
1382