Skip to content

Commit dd3932d

Browse files
tacmotacffls
andauthored
Add more tests for serialization (#409)
* add more tests for serialization * minor fix * fix failed test cases in python 3.10 * Remove impossible test cases --------- Co-authored-by: Jerry <jerrycgh@gmail.com>
1 parent d00442e commit dd3932d

File tree

2 files changed

+236
-15
lines changed

2 files changed

+236
-15
lines changed

pycardano/serialization.py

+12-8
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ def _check_recursive(value, type_hint):
329329
_check_recursive(k, key_type) and _check_recursive(v, value_type)
330330
for k, v in value.items()
331331
)
332-
elif origin in (list, set, tuple):
332+
elif origin in (list, set, tuple, frozenset):
333333
if value is None:
334334
return True
335335
args = type_hint.__args__
@@ -518,6 +518,7 @@ def _restore_typed_primitive(
518518
Returns:
519519
Union[:const:`Primitive`, CBORSerializable]: A CBOR primitive or a CBORSerializable.
520520
"""
521+
521522
if t is Any or (t in PRIMITIVE_TYPES and isinstance(v, t)):
522523
return v
523524
elif isclass(t) and issubclass(t, CBORSerializable):
@@ -536,16 +537,14 @@ def _restore_typed_primitive(
536537
if not isinstance(v, bytes):
537538
raise DeserializeException(f"Expected type bytes but got {type(v)}")
538539
return ByteString(v)
539-
elif isclass(t) and t.__name__ in ["PlutusV1Script", "PlutusV2Script"]:
540+
elif isclass(t) and t.__name__ in [
541+
"PlutusV1Script",
542+
"PlutusV2Script",
543+
"PlutusV3Script",
544+
]:
540545
if not isinstance(v, bytes):
541546
raise DeserializeException(f"Expected type bytes but got {type(v)}")
542547
return t(v)
543-
544-
elif isclass(t) and issubclass(t, IndefiniteList):
545-
try:
546-
return IndefiniteList(v)
547-
except TypeError:
548-
raise DeserializeException(f"Can not initialize IndefiniteList from {v}")
549548
elif hasattr(t, "__origin__") and (t.__origin__ is dict):
550549
t_args = t.__args__
551550
if len(t_args) != 2:
@@ -572,6 +571,11 @@ def _restore_typed_primitive(
572571
raise DeserializeException(
573572
f"Cannot deserialize object: \n{v}\n in any valid type from {t_args}."
574573
)
574+
elif isclass(t) and issubclass(t, IndefiniteList):
575+
try:
576+
return t(v)
577+
except TypeError:
578+
raise DeserializeException(f"Can not initialize IndefiniteList from {v}")
575579
raise DeserializeException(f"Cannot deserialize object: \n{v}\n to type {t}.")
576580

577581

test/pycardano/test_serialization.py

+224-7
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,105 @@
1+
from collections import defaultdict, deque
12
from dataclasses import dataclass, field
23
from test.pycardano.util import check_two_way_cbor
3-
from typing import Any, Dict, List, Optional, Set, Tuple, Union
4+
from typing import Any, Deque, Dict, List, Optional, Set, Tuple, Union
45

56
import cbor2
67
import pytest
78

8-
import pycardano
9-
from pycardano import Datum, RawPlutusData
10-
from pycardano.exception import DeserializeException
9+
from pycardano import Datum, MultiAsset, RawPlutusData, Transaction
10+
from pycardano.exception import DeserializeException, SerializeException
1111
from pycardano.plutus import PlutusV1Script, PlutusV2Script
1212
from pycardano.serialization import (
1313
ArrayCBORSerializable,
14+
ByteString,
1415
CBORSerializable,
1516
DictCBORSerializable,
1617
IndefiniteList,
1718
MapCBORSerializable,
19+
RawCBOR,
20+
default_encoder,
1821
limit_primitive_type,
1922
)
2023

2124

25+
def test_hash():
26+
b1 = ByteString(b"hello")
27+
b2 = ByteString(b"hello")
28+
b3 = ByteString(b"world")
29+
30+
assert hash(b1) == hash(b2)
31+
assert hash(b1) != hash(b3)
32+
33+
34+
def test_eq_with_bytestring():
35+
b1 = ByteString(b"hello")
36+
b2 = ByteString(b"hello")
37+
b3 = ByteString(b"world")
38+
39+
assert b1 == b2
40+
assert b1 != b3
41+
42+
43+
def test_eq_with_bytes():
44+
b1 = ByteString(b"hello")
45+
46+
assert b1 == b"hello"
47+
assert b1 != b"world"
48+
49+
50+
def test_eq_with_other_object():
51+
b1 = ByteString(b"hello")
52+
53+
assert b1 != 123
54+
assert b1 != None
55+
56+
57+
def test_default_encoder_cbor_serializable():
58+
cbor2.dumps(RawCBOR(b"1234"), default=default_encoder)
59+
cbor2.dumps(ByteString(b"1234"), default=default_encoder)
60+
61+
62+
def test_default_dict_serialization():
63+
@dataclass
64+
class Test1(ArrayCBORSerializable):
65+
a: defaultdict
66+
67+
inner_dd = defaultdict(lambda: 0)
68+
inner_dd[frozenset({1, 2, 3})] = 1
69+
70+
dd = Test1(a=inner_dd)
71+
dd.to_primitive()
72+
73+
74+
def test_default_dict_serialization_2():
75+
class MutableHashable(set):
76+
def __init__(self, data):
77+
self.data = data # Mutable attribute
78+
79+
def __hash__(self):
80+
# Provide a hash based on some property
81+
return hash(self.data)
82+
83+
def __eq__(self, other):
84+
return isinstance(other, MutableHashable) and self.data == other.data
85+
86+
@dataclass
87+
class Test1(ArrayCBORSerializable):
88+
a: MutableHashable
89+
90+
dd = Test1(a=MutableHashable(1))
91+
dd.to_primitive()
92+
93+
94+
def test_default_set_serialization():
95+
@dataclass
96+
class Test1(ArrayCBORSerializable):
97+
a: set
98+
99+
dd = Test1(a=({"a", "b", "c"}))
100+
dd.to_primitive()
101+
102+
22103
@pytest.mark.single
23104
def test_limit_primitive_type():
24105
class MockClass(CBORSerializable):
@@ -67,6 +148,17 @@ class Test1(ArrayCBORSerializable):
67148
assert hasattr(t, "unknown_field0") and t.unknown_field0 == "c"
68149

69150

151+
def test_array_cbor_serializable_unknown_key():
152+
@dataclass
153+
class Test1(ArrayCBORSerializable):
154+
a: str
155+
b: str
156+
157+
values = {"a": "abc", "c": "cde"}
158+
with pytest.raises(DeserializeException):
159+
Test1.from_primitive(values)
160+
161+
70162
def test_array_cbor_serializable_optional_field():
71163
@dataclass
72164
class Test1(ArrayCBORSerializable):
@@ -100,6 +192,15 @@ class Test2(MapCBORSerializable):
100192
check_two_way_cbor(t)
101193

102194

195+
def test_map_primitive_no_key():
196+
@dataclass
197+
class Test1(MapCBORSerializable):
198+
metadata: int = field(default=None, metadata={"key": 0})
199+
200+
with pytest.raises(DeserializeException):
201+
Test1.from_primitive({1: 1})
202+
203+
103204
def test_map_cbor_serializable_custom_keys():
104205
@dataclass
105206
class Test1(MapCBORSerializable):
@@ -117,6 +218,17 @@ class Test2(MapCBORSerializable):
117218
check_two_way_cbor(t)
118219

119220

221+
def test_map_cbor_serializable_duplicate_keys():
222+
@dataclass
223+
class Test3(MapCBORSerializable):
224+
a: str = field(default="", metadata={"key": "0"})
225+
b: str = field(default="", metadata={"key": "0"})
226+
227+
obj = Test3(a="a", b="1")
228+
with pytest.raises(SerializeException):
229+
obj.to_shallow_primitive()
230+
231+
120232
def test_dict_cbor_serializable():
121233
class MyTestDict(DictCBORSerializable):
122234
KEY_TYPE = bytes
@@ -138,6 +250,15 @@ class MyTestDict(DictCBORSerializable):
138250
# Make sure the cbor of a and b are exactly the same even when their items are inserted in different orders.
139251
assert a.to_cbor_hex() == b.to_cbor_hex()
140252

253+
assert a.to_cbor_hex() != b
254+
255+
del a[b"100"]
256+
assert a.data == {b"110": 1, b"1": 3}
257+
258+
a1 = a.__copy__()
259+
assert a1.data == a.data
260+
assert a1 is not a
261+
141262

142263
def test_dict_complex_key_cbor_serializable():
143264
@dataclass(unsafe_hash=True)
@@ -210,6 +331,18 @@ class Test1(MapCBORSerializable):
210331
check_two_way_cbor(t)
211332

212333

334+
def test_unexpected_validate_type():
335+
@dataclass
336+
class Test1(CBORSerializable):
337+
a: Optional[int]
338+
b: Deque
339+
340+
dq = deque((1, 2, 3))
341+
342+
obj = Test1(a=None, b=dq)
343+
obj.validate()
344+
345+
213346
def test_wrong_primitive_type():
214347
@dataclass
215348
class Test1(MapCBORSerializable):
@@ -308,10 +441,94 @@ def test_deserialize_empty_value():
308441
"""
309442
This should not crash even though there is an empty value in the transaction
310443
"""
311-
transaction = pycardano.Transaction.from_cbor(
444+
transaction = Transaction.from_cbor(
312445
"84a300818258202c9d507f93b5e46e9c807aff51e7bf0d015430fe1df0d2c6866df5c21febaf23020183a400581d707959ca93792ac3025823f706e9b12e8201d841bb96aed97a62db62a801821a009abef6a0028201d81843d8798003d818590859820259085459085101000032323232323232323222232325333008323232323232323232325333012002100114a0a666022a66602266e1c00d200113371e00e6eb8c024c03cc008c03c038528099b8f005375c6004601e6004601e01c2940c94ccc044cdc3a400000226464a66602c6032004266e3c004dd718059808980598088080b1bae30170013758602c602e602e602e602e602e602e602e602e601e6012601e018264646464646464646464646464a66603c66e24dd69807980e1806180e180b180e00d9991191919299981199b8748008004520001375a60506042004604200264a66604466e1d200200114c103d87a8000132323300100100222533302800114c103d87a800013232323253330293371e014004266e9520003302d375000297ae0133006006003375a60540066eb8c0a0008c0b0008c0a8004dd598139810001181000099198008008051129998128008a6103d87a800013232323253330263371e010004266e9520003302a374c00297ae01330060060033756604e0066eb8c094008c0a4008c09c004dd7180b180e180b180e1806180e180b180e00d9bae300f301c3016301c300c301c3016301c01b1323300100100422533302300114a226464a666044646600200200c44a66604e00229404c8c94ccc098cdc78010030a51133004004001302b002375c60520022660080080022940c09c008dd718128008a503322323300100100322533302400110031330253026001330020023027001330213015301b3015301b300b301b3015301b01a4bd70180180099299980e99b87480052000100113233333009002014001222325333022300e00114c103d87a800013374a9000198131ba60014bd701999980580080aa400244464a66604a66e1c005200014c103d87a800013374a9000198149ba80014bd7019b8000100200e00b3232002323300100100222533302300114984c94ccc09000452613232323232323253330283370e9000000899805005198160030028b1813000998098010009bae3026003375c604a0066052006604e004604c004604c0026604266ec0dd4808a6010120004bd6f7b6301bab300d301a300d301a3232325333022302500210011630230013300c3758602a603600246464a66604066e1d200000114a02944c078004c058c070c058c070c03cc070004c050c06805cc0040108c008004c004004894ccc07400452f5c026603c6036603e002660040046040002664464646466600200200497adef6c60222533302100210011333003003302400232323330010010030022225333025002100113233300400430290033333300e002375c60480026eacc094004888c94ccc09cc04c0045300103d87a800013374a9000198159ba60014bd70191998008008018011112999816001080089919980200218180019999980a0011bae302b001375a605800244464a66605c66e1c005200014c0103d87a800013374a9000198191ba80014bd7019b80002001017302e002010302700237566046004646600200200444a66603e002297ae0133020374c6eacc034c068c084004cc008008c088004cc020dd61803980b800919baf30123018301230180013374a90001980f1ba90034bd701bae300f3015300f3015014300f3015012222223233001001006225333020001133021337606ea4018dd4002a5eb7bdb1804c8c8c8c94ccc084cdd799803805001260103d8798000133025337606ea4028dd40048028a99981099b8f00a0021323253330233370e900000089981399bb037520186050604200400a200a604200266601001401200226604a66ec0dd48011ba800133006006003375a60440066eb8c080008c090008c08800488888c8cc004004018894ccc07c0044cc080cdd81ba9006374c00a97adef6c6013232323253330203375e6600e01400498103d8798000133024337606ea4028dd30048028a99981019b8f00a0021323253330223370e900000089981319bb03752018604e604000400a200a604000266601001401200226604866ec0dd48011ba600133006006003375660420066eb8c07c008c08c008c08400494ccc0600045288a50225333015337200040022980103d8798000153330153371e0040022980103d87a800014c103d87b8000230183019301900122323300100100322533301800114bd7009919299980b980280109980d80119802002000899802002000980e001180d00098078061180a980b0009bad30130013013002375c602200260220046eb8c03c004c8c8c94ccc03cc048008400458dd618080009919198008008011129998080008a5eb804c8ccc888c8cc00400400c894ccc058004400c4c8cc060dd39980c1ba90063301837526eb8c054004cc060dd41bad30160014bd7019801801980d001180c0009bae300f00137566020002660060066028004602400264646600200200444a666020002297adef6c6013232323253330113371e9101000021003133015337606ea4008dd3000998030030019bab3012003375c6020004602800460240026eacc03cc040c040c040c040c020004c004c01c0108c038004526136563253330083370e90000008a99980598030020a4c2c2a66601066e1d20020011533300b300600414985858c01800cc8c94ccc020cdc3a4000002264646464a66601e60240042646493180380119299980699b87480000044c8c8c8c8c8c94ccc058c0640084c9263253330143370e9000000899191919299980d980f00109924c60240062c6eb4c070004c070008c068004c04800858c04800458c05c004c05c008dd7180a800980a8011bae3013001300b00416300b0031630100013010002300e001300600516300600423253330083370e9000000899191919299980798090010a4c2c6eb8c040004c040008dd7180700098030010b1803000918029baa001230033754002ae6955ceaab9e5573eae815d0aba21a400581d707959ca93792ac3025823f706e9b12e8201d841bb96aed97a62db62a801821a005788a2a0028201d81843d8798003d81859045b820259045659045301000033232323232323232322322253330073232323232323232533300f00114a22646464a66602a60300042646464646464646464a666036a666036a666036002200a2940400852808018a50533301a533301a3370e01a9001099b8f011375c6026603000a29404cdc78079bae3003301800514a066e212000375a6004602e6014602e00c66e1cc8c8c8c94ccc070cdc3a40040022900009bad3021301a002301a00132533301b3370e90010008a6103d87a8000132323300100100222533302100114c103d87a800013232323253330223371e02e004266e95200033026375000297ae0133006006003375a60460066eb8c084008c094008c08c004dd59810180c801180c800991980080080111299980f0008a6103d87a8000132323232533301f3371e02c004266e95200033023374c00297ae0133006006003375660400066eb8c078008c088008c080004dd59800980b0038059180e980f0009991191980080080191299980e8008a5013232533301c3371e00400a29444cc010010004c084008dd7180f8009bac301b301c301c301c301c301c301c301c301c3014300f3014010375c601e602800660340026034004603000260206644a66602866e1d20043013001132325333019301c0021320023253330173370e9000000899191919299980f18108010991924c601400464a66603866e1d2000001132323232323253330253028002132498c94ccc08ccdc3a4000002264646464a666054605a00426493180a8018b1bad302b001302b002302900130210021630210011630260013026002375c604800260480046eb8c088004c06801058c06800c58c07c004c07c008c074004c05400858c05400458c068004c048004588c94ccc050cdc3a4000002264646464a666036603c0042930b1bae301c001301c002375c603400260240042c6024002600660200022c602c00264646600200200444a66602c002297ae0132325333015323253330173370e9001000899b8f013375c6038602a0042940c054004c038c04cc038c04c0084cc064008cc0100100044cc010010004c068008c060004dd618009807180498070051180a980b180b00099b8700148004dd6980900098090011bae30100013010002375c601c002646464a66601c602200420022c6eb0c03c004c8c8cc004004008894ccc03c00452f5c0264666444646600200200644a66602a00220062646602e6e9ccc05cdd48031980b9ba9375c60280026602e6ea0dd6980a800a5eb80cc00c00cc064008c05c004dd718070009bab300f001330030033013002301100132323300100100222533300f00114bd6f7b630099191919299980819b8f489000021003133014337606ea4008dd3000998030030019bab3011003375c601e004602600460220026eacc038c03cc03cc03cc03cc01c004c004c0180088c03400452613656375c0024600a6ea80048c00cdd5000ab9a5573aaae7955cfaba05742ae8930011e581c44942234591eee784947f20ab51826428bac8589b372aa9086ee49ee0001825839002b630067f4997129c6fcaea188d4535b1e69fa5860382ba1d2889c3ff3c5cf4a64da3d8d3ac295c3bcbe125961078cdb83080c2a722ef03b1b000000024ab36c59021a0004c829a1008182582064e1867f2c8e8da2e7f5bc06c2224dfdb51b9a40d895d556045ba696483c69685840525f47c570dcfee996eb41a726d1a25e19f46730dccc8e3c05b7dbcf98ceede1ce7931e3b7e0eee9f06b15351042c7285412ac8e4e624ee71805d1a926c65f02f5f6"
313446
)
314447
assert (
315-
transaction.transaction_body.outputs[0].amount.multi_asset
316-
== pycardano.MultiAsset()
448+
transaction.transaction_body.outputs[0].amount.multi_asset == MultiAsset()
317449
), "Invalid deserialization of multi asset"
450+
451+
452+
def test_restore_typed_primitive():
453+
@dataclass
454+
class Test1(ArrayCBORSerializable):
455+
a: ByteString
456+
b: str
457+
458+
t = Test1.from_primitive([b"123", "b"])
459+
460+
461+
def test_restore_typed_byte_primitive():
462+
@dataclass
463+
class Test1(ArrayCBORSerializable):
464+
a: ByteString
465+
b: str
466+
467+
with pytest.raises(DeserializeException):
468+
t = Test1.from_primitive(["123", "b"])
469+
470+
471+
def test_restore_typed_list_primitive():
472+
@dataclass
473+
class Test1(ArrayCBORSerializable):
474+
a: List[str]
475+
476+
with pytest.raises(DeserializeException):
477+
t = Test1.from_primitive([[1]])
478+
479+
480+
def test_restore_typed_plutus_script_primitive():
481+
@dataclass
482+
class Test1(ArrayCBORSerializable):
483+
a: PlutusV1Script
484+
b: str
485+
486+
with pytest.raises(DeserializeException):
487+
t = Test1.from_primitive(["123", "b"])
488+
489+
490+
def test_restore_typed_indefinitelist():
491+
class Test1(IndefiniteList):
492+
pass
493+
494+
@dataclass
495+
class Test2(ArrayCBORSerializable):
496+
a: Test1
497+
498+
t = Test2.from_primitive([[1, 2, 3]])
499+
assert t.a.data == [1, 2, 3]
500+
assert isinstance(t.a, Test1)
501+
502+
503+
def test_restore_typed_not_dict_primitive():
504+
@dataclass
505+
class Test1(ArrayCBORSerializable):
506+
a: Dict[str, int]
507+
508+
with pytest.raises(DeserializeException):
509+
t = Test1.from_primitive(["a"])
510+
511+
512+
def test_restore_typed_union_primitive():
513+
@dataclass
514+
class Test1(ArrayCBORSerializable):
515+
a: Union[int, list]
516+
517+
with pytest.raises(DeserializeException):
518+
t = Test1.from_primitive(["a"])
519+
520+
521+
def test_copy():
522+
@dataclass
523+
class Test1(ArrayCBORSerializable):
524+
a: int
525+
b: str
526+
527+
class Test2(DictCBORSerializable):
528+
KEY_TYPE = str
529+
VALUE_TYPE = Test1
530+
531+
t = Test2()
532+
t["x"] = Test1(a=1, b="x")
533+
t["y"] = Test1(a=2, b="y")
534+
t.copy()

0 commit comments

Comments
 (0)