StatusHandlerTest::handleProvider()   B
last analyzed

Complexity

Conditions 4
Paths 8

Size

Total Lines 47
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 1
Metric Value
c 2
b 1
f 1
dl 0
loc 47
rs 8.6846
cc 4
eloc 26
nc 8
nop 0
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace BaleenTest\Baleen\CommandBus\Config;
21
22
use Baleen\Cli\CommandBus\Config\StatusHandler;
23
use Baleen\Cli\CommandBus\Config\StatusMessage;
24
use Baleen\Cli\Config\ConfigStorage;
25
use Baleen\Migrations\Migration\MigrationInterface;
26
use Baleen\Migrations\Repository\RepositoryInterface;
27
use Baleen\Migrations\Storage\StorageInterface;
28
use Baleen\Migrations\Version as V;
29
use Baleen\Migrations\Version;
30
use Baleen\Migrations\Version\Collection\LinkedVersions;
31
use Baleen\Migrations\Version\Collection\MigratedVersions;
32
use Baleen\Migrations\Version\Comparator\DefaultComparator;
33
use BaleenTest\Cli\CommandBus\HandlerTestCase;
34
use Mockery as m;
35
36
/**
37
 * Class StatusHandlerTest
38
 * @author Gabriel Somoza <[email protected]>
39
 *
40
 * @property StatusMessage|m\Mock command
41
 */
42
class StatusHandlerTest extends HandlerTestCase
43
{
44
    /** @var m\Mock|ConfigStorage */
45
    protected $configStorage;
46
47
    /** @var m\Mock|RepositoryInterface */
48
    protected $repository;
49
50
    /** @var m\Mock|StorageInterface */
51
    protected $storage;
52
53
    /** @var m\Mock|callable */
54
    protected $comparator;
55
56
    /** @var m\Mock */
57
    protected $comparatorExpectation;
58
59
    /**
60
     * setUp
61
     */
62
    public function setUp()
63
    {
64
        $this->instance = m::mock(StatusHandler::class)
65
            ->shouldAllowMockingProtectedMethods()
66
            ->makePartial();
67
        $this->command = m::mock(StatusMessage::class)->makePartial();
68
        $this->configStorage = m::mock(ConfigStorage::class);
69
        $this->command->setConfigStorage($this->configStorage);
70
        $this->repository = m::mock(RepositoryInterface::class);
71
        $this->command->setRepository($this->repository);
72
        $this->storage = m::mock(StorageInterface::class);
73
        $this->command->setStorage($this->storage);
74
        $this->command->setComparator(new DefaultComparator());
75
76
        parent::setUp();
77
    }
78
79
    /**
80
     * testPrintPendingVersions
81
     * @param $id
82
     * @param $migrationClass
83
     * @param $style
84
     * @dataProvider printPendingVersionProvider
85
     */
86
    public function testPrintPendingVersion($id, $migrationClass, $style)
87
    {
88
        $version = m::mock(V::class);
89
        $version->shouldReceive([
90
            'getId' => $id,
91
            'getMigration' => $migrationClass,
92
        ])->once();
93
        if ($migrationClass === '__INVALID__') {
94
            $this->setExpectedException(\ReflectionException::class);
95
        } else {
96
            $this->setPropVal('output', $this->output, $this->instance);
97
            $this->output->shouldReceive('writeln')->once()->with("/^.*?\\<$style\\>.*?$id.*?\\<\\/$style\\>.*?$/");
98
        }
99
        $this->invokeMethod('printPendingVersion', $this->instance, [$version, $style]);
100
    }
101
102
    /**
103
     * printPendingVersionsProvider
104
     * @return array
105
     */
106
    public function printPendingVersionProvider()
107
    {
108
        $ids = [123];
109
        $migrationClasses = ['stdClass', '__INVALID__'];
110
        $styles = [StatusHandler::STYLE_COMMENT, StatusHandler::STYLE_INFO];
111
        return $this->combinations([$ids, $migrationClasses, $styles]);
112
    }
113
114
    /**
115
     * testPrintDiff
116
     * @param $versions
117
     * @param $message
118
     * @param $style
119
     * @dataProvider printDiffProvider
120
     */
121
    public function testPrintDiff(array $versions, $message, $style)
122
    {
123
        if (count($versions)) {
124
            $this->instance
125
                ->shouldReceive('printPendingVersion')
126
                ->times(count($versions))
127
                ->with(m::type(V::class), $style);
128
            $this->output->shouldReceive('writeln')->once()->with($message);
129
            $this->output->shouldReceive('writeln')->twice()->with('');
130
        } else {
131
            $this->output->shouldNotReceive('writeln');
132
        }
133
        $this->setPropVal('output', $this->output, $this->instance);
134
        $this->invokeMethod('printDiff', $this->instance, [$versions, $message, $style]);
135
    }
136
137
    /**
138
     * printDiffProvider
139
     * @return array
140
     */
141
    public function printDiffProvider()
142
    {
143
        $versions = [[], [m::mock(V::class), m::mock(V::class)]];
144
        $messages = ['Single Message', ['Multiple', 'Messages']];
145
        $styles = [StatusHandler::STYLE_INFO, StatusHandler::STYLE_COMMENT];
146
        return $this->combinations([$versions, $messages, $styles]);
147
    }
148
149
    /**
150
     * testSplitDiff
151
     * @param V[] $diff
152
     * @param callable $comparator
153
     * @param V $head
154
     * @dataProvider splitDiffProvider
155
     */
156
    public function testSplitDiff(array $diff, callable $comparator, V $head)
157
    {
158
        list($beforeHead, $afterHead) = $this->invokeMethod('splitDiff', $this->instance, [$diff, $comparator, $head]);
159
        if (empty($diff)) {
160
            $this->assertEmpty($beforeHead);
161
            $this->assertEmpty($afterHead);
162
        }
163
        foreach ($beforeHead as $v) {
164
            $this->assertLessThan(0, $comparator($v, $head));
165
        }
166
        foreach ($afterHead as $v) {
167
            $this->assertGreaterThan(0, $comparator($v, $head));
168
        }
169
    }
170
171
    /**
172
     * splitDiffProvider
173
     * @return array
174
     */
175
    public function splitDiffProvider()
176
    {
177
        $defaultComparator = new DefaultComparator();
178
        $diffs = [[], V::fromArray(range(2, 10))];
179
        $comparators = [$defaultComparator];
180
        $heads = V::fromArray(1, 2, 5, 10, 11);
181
        return $this->combinations([$diffs, $comparators, $heads]);
182
    }
183
184
    /**
185
     * testHandle
186
     *
187
     * @param LinkedVersions $available
188
     * @param MigratedVersions $migrated
189
     * @param $pendingCount
190
     *
191
     * @dataProvider handleProvider
192
     */
193
    public function testHandle(LinkedVersions $available, MigratedVersions $migrated, $pendingCount)
194
    {
195
        $this->repository->shouldReceive('fetchAll')->once()->andReturn($available);
196
        $this->storage->shouldReceive('fetchAll')->once()->andReturn($migrated);
197
        $this->command->setRepository($this->repository);
198
        $this->command->setStorage($this->storage);
199
        $currentMsg = $migrated->last() === false ?
200
            '/[Nn]othing has been migrated/' :
201
            '/[Cc]urrent version.*?' . $migrated->last()->getId() . '.*?$/';
202
        $this->output->shouldReceive('writeln')->once()->with($currentMsg);
203
        if ($pendingCount > 0) {
204
            $this->output->shouldReceive('writeln')->with(m::on(function($messages) use ($pendingCount) {
205
                return preg_match("/out\\-of\\-date.*?by $pendingCount versions/", $messages[0])
206
                    && is_string($messages[1])
207
                    && $messages[2] === '';
208
            }))->once();
209
            $this->instance->shouldReceive('printDiff')->with(
210
                m::type('array'),
211
                m::on(function($messages) {
212
                    return preg_match('/still pending.*?:$/', $messages[0])
213
                        && preg_match('/use.*?migrate.*?to migrate them/', $messages[1]);
214
                }),
215
                StatusHandler::STYLE_COMMENT
216
            )->once();
217
            $this->instance->shouldReceive('printDiff')->with(
218
                m::type('array'),
219
                m::on(function($messages) {
220
                    return (bool) preg_match('/[Nn]ew migrations:$/', $messages[0]);
221
                }),
222
                StatusHandler::STYLE_INFO
223
            )->once();
224
        } else {
225
            $this->output->shouldReceive('writeln')->with('/up\\-to\\-date/')->once();
226
        }
227
        $this->handle();
228
    }
229
230
    /**
231
     * handleProvider
232
     * @return array
233
     */
234
    public function handleProvider()
235
    {
236
        // Calculate combinations of different repository and storage states.
237
        // All test-cases here should assume sequential execution of migrations, so that we can easily calculate
238
        // the number of pending migrations with the foreach loop below (see comment below).
239
        $repVersions = [
240
            [],
241
            Version::fromArray(range(1,10)),
242
        ];
243
        $repositories = [];
244
        foreach ($repVersions as $versions) {
245
            $this->linkVersions($versions);
246
            $repositories[] = new LinkedVersions($versions);
247
        }
248
        $storageVersions = [
249
            [],
250
            Version::fromArray(range(1,3)),
251
        ];
252
        $storages = [];
253
        foreach ($storageVersions as $versions) {
254
            $this->linkVersions($versions, true);
255
            $storages[] = new MigratedVersions($versions);
256
        }
257
258
        $combinations = $this->combinations([$repositories, $storages]);
259
260
        // calculate pending number of migrations and set that as the third parameter. See note in function header.
261
        foreach($combinations as &$combination) { // NB: addressing by reference!
262
            $pending = $combination[0]->count() - $combination[1]->count();
263
            $combination[] = $pending;
264
        }
265
266
        // Additional "special" use-cases below. Pending count (third parameter) should be set manually.
267
268
        // Test case for https://github.com/baleen/cli/issues/23
269
        $repositoryVersions23 = [new V(1), new V(3)];
270
        $this->linkVersions($repositoryVersions23);
271
        $storageVersions23 = [new V(1), new V(2)];
272
        $this->linkVersions($storageVersions23, true);
273
        $combinations[] = [
274
            new LinkedVersions($repositoryVersions23),
275
            new MigratedVersions($storageVersions23),
276
            1 // one migration pending: v3
277
        ];
278
279
        return $combinations;
280
    }
281
282
    /**
283
     * linkVersions
284
     * @param $versions
285
     * @param bool $migrated
286
     */
287
    protected function linkVersions(&$versions, $migrated = false)
288
    {
289
        foreach ($versions as $v) {
290
            /** @var Version $v */
291
            /** @var MigrationInterface|m\Mock $migration */
292
            $migration = m::mock(MigrationInterface::class);
293
            $v->setMigration($migration);
294
            $v->setMigrated($migrated);
295
        }
296
    }
297
298
    /**
299
     * testGetRelativePath
300
     * @param $from
301
     * @param $to
302
     * @param $expected
303
     * @dataProvider getRelativePathProvider
304
     */
305
    public function testGetRelativePath($from, $to, $expected)
306
    {
307
        $result = $this->invokeMethod('getRelativePath', $this->instance, [$from, $to]);
308
        $this->assertEquals($expected, $result);
309
    }
310
311
    /**
312
     * getRelativePathProvider
313
     * @return array
314
     */
315
    public function getRelativePathProvider()
316
    {
317
        $file1 = '/var/log/http/access.log';
318
        $file2 = '/etc/apache/config.d/httpd.conf';
319
        $file3 = '/var/log/http/website/error.log';
320
        $dir1 = '/var';
321
        return [
322
            ['', '', ''],                           // empty
323
            ['/', '/', ''],                         // from root to root
324
            ['/var', '/var', ''],                   // from directory to directory
325
            ['/var/log.php', '/var/log.php', ''],   // from file to file
326
            ['/', '/var/log.php', 'var/log.php'],   // from root to file
327
            ['', '/var/log.php', 'var/log.php'],    // without $from (same as from root)
328
            ['/var', '', '..'],                     // towards root
329
            ['/var', '', '..'],                     // without $to (same as towards root)
330
            [$file1, $file2, '../../..' . $file2],  // backwards traversal
331
            [$file1, $file3, 'website/error.log'],  // inward traversal
332
            [$dir1, $file1, 'log/http/access.log'], // inward traversal from directory
333
        ];
334
    }
335
}
336