Completed
Push — develop ( b4a21c...b1df34 )
by Jace
02:37
created

MockConverter   A

Complexity

Total Complexity 3

Size/Duplication

Total Lines 14
Duplicated Lines 0 %
Metric Value
wmc 3
dl 0
loc 14
rs 10

3 Methods

Rating   Name   Duplication   Size   Complexity  
A create_default() 0 3 1
A to_value() 0 3 1
A to_data() 0 3 1
1
# pylint: disable=unused-variable,expression-not-assigned
2
# pylint: disable=missing-docstring,no-self-use,no-member,misplaced-comparison-constant
0 ignored issues
show
introduced by
Bad option value 'misplaced-comparison-constant'
Loading history...
3
4
import logging
5
from unittest.mock import patch, Mock
6
7
import pytest
8
from expecter import expect
0 ignored issues
show
Configuration introduced by
The import expecter could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
9
10
from yorm import exceptions
11
from yorm import decorators
12
from yorm.bases import Converter
13
14
log = logging.getLogger(__name__)
15
16
17
class MockConverter(Converter):
18
    """Sample converter class."""
19
20
    @classmethod
21
    def create_default(cls):
22
        return None
23
24
    @classmethod
25
    def to_value(cls, *_):
26
        return None
27
28
    @classmethod
29
    def to_data(cls, _):
30
        return None
31
32
33
@patch('yorm.diskutils.write', Mock())
34
@patch('yorm.diskutils.stamp', Mock())
35
@patch('yorm.diskutils.read', Mock(return_value=""))
36
class TestSyncObject:
37
    """Unit tests for the `sync_object` function."""
38
39
    class Sample:
40
        """Sample class."""
41
42
    def test_no_attrs(self):
43
        """Verify mapping can be enabled with no attributes."""
44
        sample = decorators.sync(self.Sample(), "sample.yml")
45
        assert "sample.yml" == sample.__mapper__.path
46
        assert {} == sample.__mapper__.attrs
47
48
    def test_with_attrs(self):
49
        """Verify mapping can be enabled with with attributes."""
50
        attrs = {'var1': MockConverter}
51
        sample = decorators.sync(self.Sample(), "sample.yml", attrs)
52
        assert "sample.yml" == sample.__mapper__.path
53
        assert {'var1': MockConverter} == sample.__mapper__.attrs
54
55
    def test_multiple(self):
56
        """Verify mapping cannot be enabled twice."""
57
        sample = decorators.sync(self.Sample(), "sample.yml")
58
        with pytest.raises(exceptions.MappingError):
59
            decorators.sync(sample, "sample.yml")
60
61
    @patch('yorm.diskutils.exists', Mock(return_value=True))
62
    def test_init_existing(self):
63
        """Verify an existing file is read."""
64
        with patch('yorm.diskutils.read', Mock(return_value="abc: 123")):
65
            sample = decorators.sync(self.Sample(), "sample.yml", strict=False)
66
        assert 123 == sample.abc
67
68
    @patch('yorm.diskutils.exists', Mock(return_value=False))
69
    def test_exception_when_file_expected_but_missing(self):
70
        decorators.sync(self.Sample(), "sample.yml", existing=False)
71
        with pytest.raises(exceptions.FileMissingError):
72
            decorators.sync(self.Sample(), "sample.yml", existing=True)
73
74
    @patch('yorm.diskutils.exists', Mock(return_value=True))
75
    def test_exception_when_file_not_expected_but_found(self):
76
        decorators.sync(self.Sample(), "sample.yml", existing=True)
77
        with pytest.raises(exceptions.FileAlreadyExistsError):
78
            decorators.sync(self.Sample(), "sample.yml", existing=False)
79
80
81
@patch('yorm.diskutils.write', Mock())
82
@patch('yorm.diskutils.stamp', Mock())
83
@patch('yorm.diskutils.read', Mock(return_value=""))
84
class TestSyncInstances:
85
    """Unit tests for the `sync_instances` decorator."""
86
87
    @decorators.sync("sample.yml", strict=False)
88
    class SampleDecorated:
89
        """Sample decorated class using a single path."""
90
91
        def __repr__(self):
92
            return "<decorated {}>".format(id(self))
93
94
    @decorators.sync("{UUID}.yml")
95
    class SampleDecoratedIdentifiers:
96
        """Sample decorated class using UUIDs for paths."""
97
98
        def __repr__(self):
99
            return "<decorated w/ UUID {}>".format(id(self))
100
101
    @decorators.sync("path/to/{n}.yml", {'n': 'name'})
102
    class SampleDecoratedAttributes:
103
        """Sample decorated class using an attribute value for paths."""
104
105
        def __init__(self, name):
106
            self.name = name
107
108
        def __repr__(self):
109
            return "<decorated w/ specified attributes {}>".format(id(self))
110
111
    @decorators.sync("path/to/{self.name}.yml")
112
    class SampleDecoratedAttributesAutomatic:
113
        """Sample decorated class using an attribute value for paths."""
114
115
        def __init__(self, name):
116
            self.name = name
117
118
        def __repr__(self):
119
            return "<decorated w/ automatic attributes {}>".format(id(self))
120
121
    @decorators.sync("{self.a}/{self.b}/{c}.yml", {'self.b': 'b', 'c': 'c'})
122
    class SampleDecoratedAttributesCombination:
123
        """Sample decorated class using an attribute value for paths."""
124
125
        def __init__(self, a, b, c):
126
            self.a = a
127
            self.b = b
128
            self.c = c
129
130
        def __repr__(self):
131
            return "<decorated w/ attributes {}>".format(id(self))
132
133
    @decorators.sync("sample.yml", attrs={'var1': MockConverter})
134
    class SampleDecoratedWithAttributes:
135
        """Sample decorated class using a single path."""
136
137
    @decorators.sync("sample.yml", attrs={'var1': MockConverter}, auto=False)
138
    class SampleDecoratedWithAttributesAutoOff:
139
        """Sample decorated class using a single path."""
140
141
    def test_no_attrs(self):
142
        """Verify mapping can be enabled with no attributes."""
143
        sample = self.SampleDecorated()
144
        assert "sample.yml" == sample.__mapper__.path
145
        assert {} == sample.__mapper__.attrs
146
147
    def test_with_attrs(self):
148
        """Verify mapping can be enabled with with attributes."""
149
        sample = self.SampleDecoratedWithAttributes()
150
        assert "sample.yml" == sample.__mapper__.path
151
        assert ['var1'] == list(sample.__mapper__.attrs.keys())
152
153
    @patch('yorm.diskutils.exists', Mock(return_value=True))
154
    def test_init_existing(self):
155
        """Verify an existing file is read."""
156
        with patch('yorm.diskutils.read', Mock(return_value="abc: 123")):
157
            sample = self.SampleDecorated()
158
        assert 123 == sample.abc
159
160
    @patch('uuid.uuid4', Mock(return_value=Mock(hex='abc123')))
161
    def test_filename_uuid(self):
162
        """Verify UUIDs can be used for filename."""
163
        sample = self.SampleDecoratedIdentifiers()
164
        assert "abc123.yml" == sample.__mapper__.path
165
        assert {} == sample.__mapper__.attrs
166
167
    def test_filename_attributes(self):
168
        """Verify attributes can be used to determine filename."""
169
        sample1 = self.SampleDecoratedAttributes('one')
170
        sample2 = self.SampleDecoratedAttributes('two')
171
        assert "path/to/one.yml" == sample1.__mapper__.path
172
        assert "path/to/two.yml" == sample2.__mapper__.path
173
174
    def test_filename_attributes_automatic(self):
175
        """Verify attributes can be used to determine filename (auto)."""
176
        sample1 = self.SampleDecoratedAttributesAutomatic('one')
177
        sample2 = self.SampleDecoratedAttributesAutomatic('two')
178
        assert "path/to/one.yml" == sample1.__mapper__.path
179
        assert "path/to/two.yml" == sample2.__mapper__.path
180
181
    def test_filename_attributes_combination(self):
182
        """Verify attributes can be used to determine filename (combo)."""
183
        log.info("Creating first object...")
184
        sample1 = self.SampleDecoratedAttributesCombination('A', 'B', 'C')
185
        log.info("Creating second object...")
186
        sample2 = self.SampleDecoratedAttributesCombination(1, 2, 3)
187
        assert "A/B/C.yml" == sample1.__mapper__.path
188
        assert "1/2/3.yml" == sample2.__mapper__.path
189
190
191
def describe_attr():
192
193
    class MockConverter1(MockConverter):
194
        """Sample converter class."""
195
196
    class MockConverter2(MockConverter):
197
        """Sample converter class."""
198
199
    @pytest.fixture
200
    def path(tmpdir):
201
        tmpdir.chdir()
202
        return "mock/path"
203
204
    def it_accepts_one_argument(path):
205
206
        @decorators.attr(var1=MockConverter1)
207
        @decorators.sync(path)
208
        class SampleDecoratedSingle:
209
            """Class using single `attr` decorator."""
210
211
        sample = SampleDecoratedSingle()
212
        expect(sample.__mapper__.attrs) == {'var1': MockConverter1}
213
214
    def it_rejects_zero_arguments():
215
        with expect.raises(ValueError):
216
            decorators.attr()
217
218
    def it_rejects_more_than_one_argument():
219
        with expect.raises(ValueError):
220
            decorators.attr(foo=1, bar=2)
221
222
    def it_can_be_applied_multiple_times(path):
223
224
        @decorators.attr(var1=MockConverter1)
225
        @decorators.attr(var2=MockConverter2)
226
        @decorators.sync(path)
227
        class SampleDecoratedMultiple:
228
            """Class using multiple `attr` decorators."""
229
230
        sample = SampleDecoratedMultiple()
231
        expect(sample.__mapper__.attrs) == {'var1': MockConverter1,
232
                                            'var2': MockConverter2}
233
234
    def it_can_be_applied_before_sync(path):
235
236
        @decorators.attr(var2=MockConverter2)
237
        @decorators.sync(path, attrs={'var1': MockConverter1})
238
        class SampleDecoratedCombo:
239
            """Class using `attr` decorator and providing a mapping."""
240
241
        sample = SampleDecoratedCombo()
242
        expect(sample.__mapper__.attrs) == {'var1': MockConverter1,
243
                                            'var2': MockConverter2}
244
245
    def it_can_be_applied_after_sync(path):
246
247
        @decorators.sync(path, attrs={'var1': MockConverter1})
248
        @decorators.attr(var2=MockConverter2)
249
        class SampleDecoratedBackwards:
250
            """Class using `attr` decorator after `sync` decorator."""
251
252
        sample = SampleDecoratedBackwards()
253
        expect(sample.__mapper__.attrs) == {'var1': MockConverter1,
254
                                            'var2': MockConverter2}
255