1
+ import pytest
2
+ import re
3
+
4
+ from luxtronik .datatypes import Base
5
+ from luxtronik .definitions .holdings import HOLDINGS_DEFINITIONS_LIST
6
+ from luxtronik .definitions .inputs import INPUTS_DEFINITIONS_LIST
7
+
8
+ KEY_IDX = "index"
9
+ KEY_COUNT = "count"
10
+ KEY_NAMES = "names"
11
+ KEY_TYPE = "type"
12
+ KEY_WRT = "writeable"
13
+ KEY_SINCE = "since"
14
+ KEY_UNTIL = "until"
15
+ KEY_DESC = "description"
16
+
17
+
18
+ class RunTestDefinitions :
19
+
20
+ # override this
21
+ definitions = None
22
+
23
+ def get_names (self , definition ):
24
+ names = definition .get (KEY_NAMES , [])
25
+ if isinstance (names , str ):
26
+ names = [names ]
27
+ return names
28
+
29
+ def test_structure (self ):
30
+ for definition in self .definitions :
31
+ assert KEY_IDX in definition , \
32
+ f"{ KEY_IDX } not defined: { definition } "
33
+ assert isinstance (definition [KEY_IDX ], int ), \
34
+ f"{ KEY_IDX } must be of type 'int': { definition } "
35
+
36
+ if KEY_COUNT in definition :
37
+ assert isinstance (definition [KEY_COUNT ], int ), \
38
+ f"{ KEY_COUNT } must be of type 'int': { definition } "
39
+
40
+ if KEY_NAMES in definition :
41
+ assert isinstance (definition [KEY_NAMES ], list ) \
42
+ or isinstance (definition [KEY_NAMES ], str ), \
43
+ f"{ KEY_NAMES } must be of type 'list' or 'str': { definition } "
44
+ if isinstance (definition [KEY_NAMES ], list ):
45
+ for name in definition [KEY_NAMES ]:
46
+ assert isinstance (name , str ), f"Entry of { KEY_NAMES } " \
47
+ f"must be of type 'int': { definition } "
48
+
49
+ if KEY_TYPE in definition :
50
+ assert issubclass (definition [KEY_TYPE ], Base ), \
51
+ f"{ KEY_TYPE } must be inherit from 'Base': { definition } "
52
+
53
+ if KEY_WRT in definition :
54
+ assert isinstance (definition [KEY_WRT ], bool ), \
55
+ f"{ KEY_WRT } must be of type 'bool': { definition } "
56
+
57
+ if KEY_SINCE in definition :
58
+ assert isinstance (definition [KEY_SINCE ], str ), \
59
+ f"{ KEY_SINCE } must be of type 'str': { definition } "
60
+
61
+ if KEY_UNTIL in definition :
62
+ assert isinstance (definition [KEY_UNTIL ], str ), \
63
+ f"{ KEY_UNTIL } must be of type 'str': { definition } "
64
+
65
+ if KEY_DESC in definition :
66
+ assert isinstance (definition [KEY_DESC ], str ), \
67
+ f"{ KEY_DESC } must be of type 'str': { definition } "
68
+
69
+ def test_index (self ):
70
+ for definition in self .definitions :
71
+ idx = definition .get (KEY_IDX , 0 )
72
+ assert idx >= 0 , \
73
+ f"The index must be greater or equal than zero: { definition } "
74
+
75
+ def test_index_ascending (self ):
76
+ prev = self .definitions [0 ]
77
+
78
+ for definition in self .definitions :
79
+ prev_idx = prev .get (KEY_IDX , - 1 )
80
+ this_idx = definition .get (KEY_IDX , 0 )
81
+ assert prev_idx <= this_idx , \
82
+ f"The definitions must be arranged in ascending order. " \
83
+ f"This allows us to avoid sorting them afterwards." \
84
+ f"this = { definition } " \
85
+ f"other = { prev } "
86
+ prev = definition
87
+
88
+ def test_count (self ):
89
+ for definition in self .definitions :
90
+ count = definition .get (KEY_COUNT , 1 )
91
+ assert count > 0 , \
92
+ f"Count field must be greater than zero: { definition } "
93
+
94
+ def test_name_valid (self ):
95
+ for definition in self .definitions :
96
+ names = self .get_names (definition )
97
+ for name in names :
98
+ sanitized = re .sub (r"[^a-z0-9_]" , "" , name )
99
+ assert sanitized == name , \
100
+ f"The name may only contain a-z0-9_"
101
+ assert sanitized != "" , \
102
+ f"Name must not be empty."
103
+
104
+ def test_name_unique (self ):
105
+ length = len (self .definitions )
106
+
107
+ for i in range (length ):
108
+ i_def = self .definitions [i ]
109
+ i_names = self .get_names (i_def )
110
+ for j in range (i + 1 , length ):
111
+ j_def = self .definitions [j ]
112
+ j_names = self .get_names (j_def )
113
+ for i_name in i_names :
114
+ for j_name in j_names :
115
+ assert i_name != j_name , \
116
+ f"All names of the same type must be unique. " \
117
+ f"this = { i_def } " \
118
+ f"other = { j_def } "
119
+
120
+ def test_data_type (self ):
121
+ for definition in self .definitions :
122
+ if KEY_TYPE in definition :
123
+ data_type = definition .get (KEY_TYPE , Base )
124
+ assert data_type is not None , \
125
+ f"Type must be set."
126
+
127
+ ###############################################################################
128
+ # Tests
129
+ ###############################################################################
130
+
131
+ class TestHoldingsDefinitions (RunTestDefinitions ):
132
+
133
+ definitions = HOLDINGS_DEFINITIONS_LIST
134
+
135
+
136
+ class TestInputsDefinitions (RunTestDefinitions ):
137
+
138
+ definitions = INPUTS_DEFINITIONS_LIST
0 commit comments