Completed
Pull Request — master (#114)
by Bart
02:11
created

ModelMapperTest   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 286
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
wmc 14
lcom 1
cbo 7
dl 0
loc 286
rs 10
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A _before() 0 4 1
A testSuccessfulExport() 0 9 1
A testUnSuccessfulImport() 0 9 1
A testSuccessfulImport() 0 9 1
A testSuccessfulImportWithForceOption() 0 10 1
A testUnsuccessfulImportWithForceOption() 0 10 1
B provideExportModels() 0 30 1
B provideImportModels() 0 34 1
A getMockModelDefinition() 0 12 1
A getMockModel() 0 13 1
A getMockConverter() 0 12 1
A expectConverter() 0 6 1
A expectSaves() 0 6 1
A expectDeletes() 0 5 1
1
<?php
2
3
namespace NerdsAndCompany\Schematic\Mappers;
4
5
use Craft;
6
use craft\base\Model;
7
use Codeception\Test\Unit;
8
use NerdsAndCompany\Schematic\Schematic;
9
use NerdsAndCompany\Schematic\Converters\Models\Base as Converter;
10
11
/**
12
 * Class ModelMapperTest.
13
 *
14
 * @author    Nerds & Company
15
 * @copyright Copyright (c) 2015-2017, Nerds & Company
16
 * @license   MIT
17
 *
18
 * @see      http://www.nerds.company
19
 */
20
class ModelMapperTest extends Unit
21
{
22
    /**
23
     * @var ModelMapper
24
     */
25
    private $mapper;
26
27
    /**
28
     * Set the mapper.
29
     *
30
     * @SuppressWarnings(PHPMD.CamelCaseMethodName)
31
     */
32
    protected function _before()
33
    {
34
        $this->mapper = new ModelMapper();
35
    }
36
37
    //==============================================================================================================
38
    //=================================================  TESTS  ====================================================
39
    //==============================================================================================================
40
41
    /**
42
     * @dataProvider provideExportModels
43
     *
44
     * @param Model[] $models
45
     * @param array   $expectedResult
46
     */
47
    public function testSuccessfulExport(array $models, array $expectedResult = [])
48
    {
49
        $converter = $this->getMockConverter();
50
        $this->expectConverter($converter, count($models));
51
52
        $actualResult = $this->mapper->export($models);
53
54
        $this->assertSame($expectedResult, $actualResult);
55
    }
56
57
    /**
58
     * @dataProvider provideImportModels
59
     *
60
     * @param Model[] $existingGroups
61
     * @param array   $modelDefinitions
62
     * @param int     $saveCount
63
     */
64
    public function testUnSuccessfulImport(array $existingGroups, array $modelDefinitions, int $saveCount)
65
    {
66
        $converter = $this->getMockConverter();
67
        $this->expectConverter($converter, count($modelDefinitions));
68
        $this->expectSaves($converter, $saveCount, false);
69
        $this->expectDeletes($converter, 0);
70
71
        $this->mapper->import($modelDefinitions, $existingGroups);
72
    }
73
74
    /**
75
     * @dataProvider provideImportModels
76
     *
77
     * @param Model[] $existingGroups
78
     * @param array   $modelDefinitions
79
     * @param int     $saveCount
80
     */
81
    public function testSuccessfulImport(array $existingGroups, array $modelDefinitions, int $saveCount)
82
    {
83
        $converter = $this->getMockConverter();
84
        $this->expectConverter($converter, count($modelDefinitions));
85
        $this->expectSaves($converter, $saveCount);
86
        $this->expectDeletes($converter, 0);
87
88
        $this->mapper->import($modelDefinitions, $existingGroups);
89
    }
90
91
    /**
92
     * @dataProvider provideImportModels
93
     *
94
     * @param Model[] $existingGroups
95
     * @param array   $modelDefinitions
96
     * @param int     $saveCount
97
     * @param int     $deleteCount
98
     */
99
    public function testSuccessfulImportWithForceOption(array $existingGroups, array $modelDefinitions, int $saveCount, int $deleteCount)
100
    {
101
        Schematic::$force = true;
102
        $converter = $this->getMockConverter();
103
        $this->expectConverter($converter, count($modelDefinitions) + $deleteCount);
104
        $this->expectSaves($converter, $saveCount);
105
        $this->expectDeletes($converter, $deleteCount);
106
107
        $this->mapper->import($modelDefinitions, $existingGroups);
108
    }
109
110
    /**
111
     * @dataProvider provideImportModels
112
     *
113
     * @param Model[] $existingGroups
114
     * @param array   $modelDefinitions
115
     * @param int     $saveCount
116
     * @param int     $deleteCount
117
     */
118
    public function testUnsuccessfulImportWithForceOption(array $existingGroups, array $modelDefinitions, int $saveCount, int $deleteCount)
119
    {
120
        Schematic::$force = true;
121
        $converter = $this->getMockConverter();
122
        $this->expectConverter($converter, count($modelDefinitions) + $deleteCount);
123
        $this->expectSaves($converter, $saveCount, false);
124
        $this->expectDeletes($converter, $deleteCount);
125
126
        $this->mapper->import($modelDefinitions, $existingGroups);
127
    }
128
129
    //==============================================================================================================
130
    //==============================================  PROVIDERS  ===================================================
131
    //==============================================================================================================
132
133
    /**
134
     * @return array
135
     */
136
    public function provideExportModels()
137
    {
138
        $mockModel1 = $this->getMockModel(1);
139
        $mockModel2 = $this->getMockModel(2);
140
141
        return [
142
            'empty array' => [
143
                'models' => [],
144
                'modelDefinitions' => [],
145
            ],
146
            'single model' => [
147
                'models' => [
148
                    $mockModel1,
149
                ],
150
                'modelDefinitions' => [
151
                    'modelHandle1' => $this->getMockModelDefinition($mockModel1),
152
                ],
153
            ],
154
            'multiple models' => [
155
                'models' => [
156
                    $mockModel1,
157
                    $mockModel2,
158
                ],
159
                'modelDefinitions' => [
160
                    'modelHandle1' => $this->getMockModelDefinition($mockModel1),
161
                    'modelHandle2' => $this->getMockModelDefinition($mockModel2),
162
                ],
163
            ],
164
        ];
165
    }
166
167
    /**
168
     * @return array
169
     */
170
    public function provideImportModels()
171
    {
172
        $mockModel1 = $this->getMockModel(1);
173
        $mockModel2 = $this->getMockModel(2);
174
175
        return [
176
            'empty array' => [
177
                'models' => [],
178
                'modelDefinitions' => [],
179
                'saveCount' => 0,
180
                'deleteCount' => 0,
181
            ],
182
            'single old model' => [
183
                'models' => [
184
                    $mockModel1,
185
                ],
186
                'modelDefinitions' => [],
187
                'saveCount' => 0,
188
                'deleteCount' => 1,
189
            ],
190
            'single new model' => [
191
                'models' => [
192
                    $mockModel1,
193
                ],
194
                'modelDefinitions' => [
195
                    'modelHandle1' => $this->getMockModelDefinition($mockModel1),
196
                    'modelHandle2' => $this->getMockModelDefinition($mockModel2),
197
                ],
198
199
                'saveCount' => 1,
200
                'deleteCount' => 0,
201
            ],
202
        ];
203
    }
204
205
    //==============================================================================================================
206
    //================================================  HELPERS  ===================================================
207
    //==============================================================================================================
208
209
    /**
210
     * Get model definition for mock model.
211
     *
212
     * @param Model $mockModel
213
     *
214
     * @return array
215
     */
216
    private function getMockModelDefinition(Model $mockModel): array
217
    {
218
        return [
219
            'class' => get_class($mockModel),
220
            'attributes' => [
221
                'name' => $mockModel->name,
222
                'handle' => $mockModel->handle,
223
                'max' => 100,
224
                'other' => 'stuff',
225
            ],
226
        ];
227
    }
228
229
    /**
230
     * Get a mock model.
231
     *
232
     * @param int $modelId
233
     *
234
     * @return Mock|Model
235
     */
236
    private function getMockModel(int $modelId): Model
237
    {
238
        $mockModel = $this->getMockBuilder(Model::class)->getMock();
239
        $mockModel->expects($this->any())
240
                  ->method('__get')
241
                  ->willReturnMap([
242
                        ['id', $modelId],
243
                        ['handle', 'modelHandle'.$modelId],
244
                        ['name', 'modelName'.$modelId],
245
                  ]);
246
247
        return $mockModel;
248
    }
249
250
    /**
251
     * Get a mock converter.
252
     *
253
     * @return Converter
254
     */
255
    private function getMockConverter(): Converter
256
    {
257
        $mockConverter = $this->getMockBuilder(Converter::class)->getMock();
258
259
        $mockConverter->expects($this->any())
260
                      ->method('getRecordDefinition')
261
                      ->willReturnCallback(function ($model) {
262
                          return $this->getMockModelDefinition($model);
263
                      });
264
265
        return $mockConverter;
266
    }
267
268
    /**
269
     * Mock a converter.
270
     *
271
     * @param Mock|Converter|null $converter
272
     */
273
    private function expectConverter($converter, int $count)
274
    {
275
        Craft::$app->controller->module->expects($this->exactly($count))
276
                                       ->method('getConverter')
277
                                       ->willReturn($converter);
278
    }
279
280
    /**
281
     * Expect a number of model saves.
282
     *
283
     * @param Converter $converter
284
     * @param int       $saveCount
285
     * @param bool      $return
286
     */
287
    private function expectSaves(Converter $converter, int $saveCount, bool $return = true)
288
    {
289
        $converter->expects($this->exactly($saveCount))
0 ignored issues
show
Documentation Bug introduced by
The method expects does not exist on object<NerdsAndCompany\S...Converters\Models\Base>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
290
                  ->method('saveRecord')
291
                  ->willReturn($return);
292
    }
293
294
    /**
295
     * Expect a number of model deletes.
296
     *
297
     * @param Converter $converter
298
     * @param int       $deleteCount
299
     */
300
    private function expectDeletes(Converter $converter, int $deleteCount)
301
    {
302
        $converter->expects($this->exactly($deleteCount))
0 ignored issues
show
Documentation Bug introduced by
The method expects does not exist on object<NerdsAndCompany\S...Converters\Models\Base>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
303
                  ->method('deleteRecord');
304
    }
305
}
306