Completed
Push — develop ( 9a26e8...bda1bd )
by Jace
11s
created

TestMappableTriggers.test_insert()   A

Complexity

Conditions 3

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 3
1
# pylint: disable=missing-docstring,no-self-use,attribute-defined-outside-init,protected-access,misplaced-comparison-constant
0 ignored issues
show
introduced by
Bad option value 'misplaced-comparison-constant'
Loading history...
2
3
import logging
4
from unittest.mock import Mock
5
6
import pytest
7
8
import yorm
9
from yorm.bases import Mappable
10
from yorm.mapper import Mapper
11
from yorm.types import String, Integer, Boolean, List, Dictionary
12
13
from . import strip
14
15
16
class MockMapper(Mapper):
17
    """Mapped file with stubbed file IO."""
18
19
    def __init__(self, obj, path, attrs):
20
        super().__init__(obj, path, attrs)
21
        self._mock_file = None
22
        self._mock_modified = True
23
        self.exists = True
24
25
    def _read(self):
26
        text = self._mock_file
27
        logging.debug("Mock read:\n%s", text.strip())
28
        return text
29
30
    def _write(self, text):
31
        logging.debug("Mock write:\n%s", text.strip())
32
        self._mock_file = text
33
        self.modified = True
34
35
    @property
36
    def modified(self):
37
        return self._mock_modified
38
39
    @modified.setter
40
    def modified(self, changes):
0 ignored issues
show
Bug introduced by
Arguments number differs from overridden 'modified' method
Loading history...
41
        self._mock_modified = changes
42
43
44
# CLASSES ######################################################################
45
46
@yorm.attr(all=Integer)
47
class IntegerList(List):
48
    """List of integers."""
49
50
51
@yorm.attr(status=Boolean)
52
class StatusDictionary(Dictionary):
53
    """Dictionary of statuses."""
54
55
56
class SampleMappable(Mappable):
57
    """Sample mappable class with hard-coded settings."""
58
59
    def __init__(self):
60
        self.__mapper__ = None
61
62
        logging.debug("Initializing sample...")
63
        self.var1 = None
64
        self.var2 = None
65
        self.var3 = None
66
        self.var4 = None
67
        self.var5 = None
68
        logging.debug("Sample initialized")
69
70
        path = "mock/path/to/sample.yml"
71
        attrs = {'var1': String,
72
                 'var2': Integer,
73
                 'var3': Boolean,
74
                 'var4': IntegerList,
75
                 'var5': StatusDictionary}
76
        self.__mapper__ = MockMapper(self, path, attrs)
77
        self.__mapper__.save()
78
79
    def __repr__(self):
80
        return "<sample {}>".format(id(self))
81
82
83
# TESTS ########################################################################
84
85
86
class TestMappable:
87
    """Unit tests for the `Mappable` class."""
88
89
    def setup_method(self, _):
90
        """Create an mappable instance for tests."""
91
        self.sample = SampleMappable()
92
93
    def test_init(self):
94
        """Verify files are created after initialized."""
95
        text = self.sample.__mapper__._read()
96
        assert strip("""
97
        var1: ''
98
        var2: 0
99
        var3: false
100
        var4: []
101
        var5:
102
          status: false
103
        """) == text
104
105 View Code Duplication
    def test_set(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
106
        """Verify the file is written to after setting an attribute."""
107
        self.sample.var1 = "abc123"
108
        self.sample.var2 = 1
109
        self.sample.var3 = True
110
        self.sample.var4 = [42]
111
        self.sample.var5 = {'status': True}
112
        text = self.sample.__mapper__._read()
113
        assert strip("""
114
        var1: abc123
115
        var2: 1
116
        var3: true
117
        var4:
118
        - 42
119
        var5:
120
          status: true
121
        """) == text
122
123 View Code Duplication
    def test_set_converted(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
124
        """Verify conversion occurs when setting attributes."""
125
        self.sample.var1 = 42
126
        self.sample.var2 = "1"
127
        self.sample.var3 = 'off'
128
        self.sample.var4 = None
129
        self.sample.var5 = {'status': 1}
130
        text = self.sample.__mapper__._read()
131
        assert strip("""
132
        var1: 42
133
        var2: 1
134
        var3: false
135
        var4: []
136
        var5:
137
          status: true
138
        """) == text
139
140
    def test_set_error(self):
141
        """Verify an exception is raised when a value cannot be converted."""
142
        with pytest.raises(ValueError):
143
            self.sample.var2 = "abc"
144
145
    def test_get(self):
146
        """Verify the file is read from before getting an attribute."""
147
        text = strip("""
148
        var1: def456
149
        var2: 42
150
        var3: off
151
        """)
152
        self.sample.__mapper__._write(text)
153
        assert"def456" == self.sample.var1
154
        assert 42 == self.sample.var2
155
        assert False is self.sample.var3
156
157
    def test_error_invalid_yaml(self):
158
        """Verify an exception is raised on invalid YAML."""
159
        text = strip("""
160
        invalid: -
161
        """)
162
        self.sample.__mapper__._write(text)
163
        with pytest.raises(ValueError):
164
            print(self.sample.var1)
165
166
    def test_error_unexpected_yaml(self):
167
        """Verify an exception is raised on unexpected YAML."""
168
        text = strip("""
169
        not a dictionary
170
        """)
171
        self.sample.__mapper__._write(text)
172
        with pytest.raises(ValueError):
173
            print(self.sample.var1)
174
175
    def test_new(self):
176
        """Verify new attributes are added to the object."""
177
        self.sample.__mapper__.auto_track = True
178
        text = strip("""
179
        new: 42
180
        """)
181
        self.sample.__mapper__._write(text)
182
        assert 42 == self.sample.new
183
184
    def test_new_unknown(self):
185
        """Verify an exception is raised on new attributes w/ unknown types"""
186
        self.sample.__mapper__.auto_track = True
187
        text = strip("""
188
        new: !!timestamp 2001-12-15T02:59:43.1Z
189
        """)
190
        self.sample.__mapper__._write(text)
191
        with pytest.raises(ValueError):
192
            print(self.sample.var1)
193
194
195
class TestMappableTriggers:
196
197
    class MockDict(Mappable, dict):
198
        pass
199
200
    class MockList:
201
202
        def append(self, value):
203
            print(value)
204
205
        def insert(self, value):
206
            print(value)
207
208
    class Sample(MockDict, MockList):
209
210
        __mapper__ = Mock()
211
        __mapper__.attrs = {}
212
        __mapper__.load = Mock()
213
        __mapper__.save = Mock()
214
215
    def setup_method(self, _):
216
        """Create an mappable instance for tests."""
217
        self.sample = self.Sample()
218
        self.sample.__mapper__.load.reset_mock()
219
        self.sample.__mapper__.save.reset_mock()
220
        self.sample.__mapper__.auto_save_after_load = False
221
222
    def test_getattribute(self):
223
        with pytest.raises(AttributeError):
224
            getattr(self.sample, 'foo')
225
        assert 1 == self.sample.__mapper__.load.call_count
226
        assert 0 == self.sample.__mapper__.save.call_count
227
228
    def test_setattr(self):
229
        self.sample.__mapper__.attrs['foo'] = Mock()
230
        setattr(self.sample, 'foo', 'bar')
231
        assert 0 == self.sample.__mapper__.load.call_count
232
        assert 1 == self.sample.__mapper__.save.call_count
233
234
    def test_getitem(self):
235
        with pytest.raises(KeyError):
236
            print(self.sample['foo'])
237
        assert 1 == self.sample.__mapper__.load.call_count
238
        assert 0 == self.sample.__mapper__.save.call_count
239
240
    def test_setitem(self):
241
        self.sample['foo'] = 'bar'
242
        assert 0 == self.sample.__mapper__.load.call_count
243
        assert 1 == self.sample.__mapper__.save.call_count
244
245
    def test_delitem(self):
246
        self.sample['foo'] = 'bar'
247
        self.sample.__mapper__.save.reset_mock()
248
249
        del self.sample['foo']
250
        assert 0 == self.sample.__mapper__.load.call_count
251
        assert 1 == self.sample.__mapper__.save.call_count
252
253
    def test_append(self):
254
        self.sample.append('foo')
255
        assert 2 == self.sample.__mapper__.load.call_count
256
        assert 1 == self.sample.__mapper__.save.call_count
257
258
    def test_insert(self):
259
        self.sample.insert('foo')
260
        assert 2 == self.sample.__mapper__.load.call_count
261
        assert 1 == self.sample.__mapper__.save.call_count
262
263
    def test_iter(self):
264
        self.sample.append('foo')
265
        self.sample.append('bar')
266
        self.sample.__mapper__.load.reset_mock()
267
        self.sample.__mapper__.save.reset_mock()
268
        self.sample.__mapper__.auto_save_after_load = False
269
        self.sample.__mapper__.modified = True
270
271
        for item in self.sample:
272
            print(item)
273
        assert 1 == self.sample.__mapper__.load.call_count
274
        assert 0 == self.sample.__mapper__.save.call_count
275
276
    def test_handle_missing_mapper(self):
277
        sample = self.MockDict()
278
        sample.__mapper__ = None
279
        sample[0] = 0
280
        print(sample[0])
281
        assert None is sample.__mapper__
282