]> git.mxchange.org Git - friendica.git/blob - tests/src/Core/Storage/Repository/StorageManagerTest.php
Merge pull request #10919 from nupplaphil/feat/storage_restructuring
[friendica.git] / tests / src / Core / Storage / Repository / StorageManagerTest.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2021, the Friendica project
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica\Test\src\Core\Storage\Repository;
23
24 use Dice\Dice;
25 use Friendica\Core\Config\Capability\IManageConfigValues;
26 use Friendica\Core\Config\Type\PreloadConfig;
27 use Friendica\Core\Hook;
28 use Friendica\Core\L10n;
29 use Friendica\Core\Session\Capability\IHandleSessions;
30 use Friendica\Core\Session\Type\Memory;
31 use Friendica\Core\Storage\Exception\InvalidClassStorageException;
32 use Friendica\Core\Storage\Capability\ICanReadFromStorage;
33 use Friendica\Core\Storage\Capability\ICanWriteToStorage;
34 use Friendica\Core\Storage\Exception\StorageException;
35 use Friendica\Core\Storage\Repository\StorageManager;
36 use Friendica\Core\Storage\Type\Filesystem;
37 use Friendica\Core\Storage\Type\SystemResource;
38 use Friendica\Database\Database;
39 use Friendica\DI;
40 use Friendica\Core\Config\Factory\Config;
41 use Friendica\Core\Config\Repository;
42 use Friendica\Core\Storage\Type;
43 use Friendica\Network\HTTPClient;
44 use Friendica\Test\DatabaseTest;
45 use Friendica\Test\Util\Database\StaticDatabase;
46 use Friendica\Test\Util\VFSTrait;
47 use Friendica\Util\Profiler;
48 use org\bovigo\vfs\vfsStream;
49 use Psr\Log\LoggerInterface;
50 use Psr\Log\NullLogger;
51 use Friendica\Test\Util\SampleStorageBackend;
52
53 class StorageManagerTest extends DatabaseTest
54 {
55         use VFSTrait;
56         /** @var Database */
57         private $dba;
58         /** @var IManageConfigValues */
59         private $config;
60         /** @var LoggerInterface */
61         private $logger;
62         /** @var L10n */
63         private $l10n;
64         /** @var HTTPClient */
65         private $httpRequest;
66
67         protected function setUp(): void
68         {
69                 parent::setUp();
70
71                 $this->setUpVfsDir();
72
73                 vfsStream::newDirectory(Type\FilesystemConfig::DEFAULT_BASE_FOLDER, 0777)->at($this->root);
74
75                 $this->logger = new NullLogger();
76
77                 $profiler = \Mockery::mock(Profiler::class);
78                 $profiler->shouldReceive('startRecording');
79                 $profiler->shouldReceive('stopRecording');
80                 $profiler->shouldReceive('saveTimestamp')->withAnyArgs()->andReturn(true);
81
82                 // load real config to avoid mocking every config-entry which is related to the Database class
83                 $configFactory = new Config();
84                 $loader        = $configFactory->createConfigFileLoader($this->root->url(), []);
85                 $configCache   = $configFactory->createCache($loader);
86
87                 $this->dba = new StaticDatabase($configCache, $profiler, $this->logger);
88
89                 $configModel  = new Repository\Config($this->dba);
90                 $this->config = new PreloadConfig($configCache, $configModel);
91                 $this->config->set('storage', 'name', 'Database');
92                 $this->config->set('storage', 'filesystem_path', $this->root->getChild(Type\FilesystemConfig::DEFAULT_BASE_FOLDER)->url());
93
94                 $this->l10n = \Mockery::mock(L10n::class);
95
96                 $this->httpRequest = \Mockery::mock(HTTPClient::class);
97         }
98
99         protected function tearDown(): void
100         {
101                 $this->root->removeChild(Type\FilesystemConfig::DEFAULT_BASE_FOLDER);
102
103                 parent::tearDown();
104         }
105
106         /**
107          * Test plain instancing first
108          */
109         public function testInstance()
110         {
111                 $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n, $this->httpRequest);
112
113                 self::assertInstanceOf(StorageManager::class, $storageManager);
114         }
115
116         public function dataStorages()
117         {
118                 return [
119                         'empty' => [
120                                 'name'       => '',
121                                 'valid'      => false,
122                                 'interface'  => ICanReadFromStorage::class,
123                                 'assert'     => null,
124                                 'assertName' => '',
125                         ],
126                         'database' => [
127                                 'name'       => Type\Database::NAME,
128                                 'valid'      => true,
129                                 'interface'  => ICanWriteToStorage::class,
130                                 'assert'     => Type\Database::class,
131                                 'assertName' => Type\Database::NAME,
132                         ],
133                         'filesystem' => [
134                                 'name'       => Filesystem::NAME,
135                                 'valid'      => true,
136                                 'interface'  => ICanWriteToStorage::class,
137                                 'assert'     => Filesystem::class,
138                                 'assertName' => Filesystem::NAME,
139                         ],
140                         'systemresource' => [
141                                 'name'       => SystemResource::NAME,
142                                 'valid'      => true,
143                                 'interface'  => ICanReadFromStorage::class,
144                                 'assert'     => SystemResource::class,
145                                 'assertName' => SystemResource::NAME,
146                         ],
147                         'invalid' => [
148                                 'name'        => 'invalid',
149                                 'valid'       => false,
150                                 'interface'   => null,
151                                 'assert'      => null,
152                                 'assertName'  => '',
153                                 'userBackend' => false,
154                         ],
155                 ];
156         }
157
158         /**
159          * Test the getByName() method
160          *
161          * @dataProvider dataStorages
162          */
163         public function testGetByName($name, $valid, $interface, $assert, $assertName)
164         {
165                 if (!$valid) {
166                         $this->expectException(InvalidClassStorageException::class);
167                 }
168
169                 if ($interface === ICanWriteToStorage::class) {
170                         $this->config->set('storage', 'name', $name);
171                 }
172
173                 $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n);
174
175                 if ($interface === ICanWriteToStorage::class) {
176                         $storage = $storageManager->getWritableStorageByName($name);
177                 } else {
178                         $storage = $storageManager->getByName($name);
179                 }
180
181                 self::assertInstanceOf($interface, $storage);
182                 self::assertInstanceOf($assert, $storage);
183                 self::assertEquals($assertName, $storage);
184         }
185
186         /**
187          * Test the isValidBackend() method
188          *
189          * @dataProvider dataStorages
190          */
191         public function testIsValidBackend($name, $valid, $interface, $assert, $assertName)
192         {
193                 $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n);
194
195                 // true in every of the backends
196                 self::assertEquals(!empty($assertName), $storageManager->isValidBackend($name));
197
198                 // if it's a ICanWriteToStorage, the valid backend should return true, otherwise false
199                 self::assertEquals($interface === ICanWriteToStorage::class, $storageManager->isValidBackend($name, StorageManager::DEFAULT_BACKENDS));
200         }
201
202         /**
203          * Test the method listBackends() with default setting
204          */
205         public function testListBackends()
206         {
207                 $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n);
208
209                 self::assertEquals(StorageManager::DEFAULT_BACKENDS, $storageManager->listBackends());
210         }
211
212         /**
213          * Test the method getBackend()
214          *
215          * @dataProvider dataStorages
216          */
217         public function testGetBackend($name, $valid, $interface, $assert, $assertName)
218         {
219                 if ($interface !== ICanWriteToStorage::class) {
220                         static::markTestSkipped('only works for ICanWriteToStorage');
221                 }
222
223                 $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n);
224
225                 $selBackend = $storageManager->getWritableStorageByName($name);
226                 $storageManager->setBackend($selBackend);
227
228                 self::assertInstanceOf($assert, $storageManager->getBackend());
229         }
230
231         /**
232          * Test the method getBackend() with a pre-configured backend
233          *
234          * @dataProvider dataStorages
235          */
236         public function testPresetBackend($name, $valid, $interface, $assert, $assertName)
237         {
238                 $this->config->set('storage', 'name', $name);
239                 if ($interface !== ICanWriteToStorage::class) {
240                         $this->expectException(InvalidClassStorageException::class);
241                 }
242
243                 $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n);
244
245                 self::assertInstanceOf($assert, $storageManager->getBackend());
246         }
247
248         /**
249          * Tests the register and unregister methods for a new backend storage class
250          *
251          * Uses a sample storage for testing
252          *
253          * @see SampleStorageBackend
254          */
255         public function testRegisterUnregisterBackends()
256         {
257                 /// @todo Remove dice once "Hook" is dynamic and mockable
258                 $dice = (new Dice())
259                         ->addRules(include __DIR__ . '/../../../../../static/dependencies.config.php')
260                         ->addRule(Database::class, ['instanceOf' => StaticDatabase::class, 'shared' => true])
261                         ->addRule(IHandleSessions::class, ['instanceOf' => Memory::class, 'shared' => true, 'call' => null]);
262                 DI::init($dice);
263
264                 $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n);
265
266                 self::assertTrue($storageManager->register(SampleStorageBackend::class));
267
268                 self::assertEquals(array_merge(StorageManager::DEFAULT_BACKENDS, [
269                         SampleStorageBackend::getName(),
270                 ]), $storageManager->listBackends());
271                 self::assertEquals(array_merge(StorageManager::DEFAULT_BACKENDS, [
272                         SampleStorageBackend::getName()
273                 ]), $this->config->get('storage', 'backends'));
274
275                 self::assertTrue($storageManager->unregister(SampleStorageBackend::class));
276                 self::assertEquals(StorageManager::DEFAULT_BACKENDS, $this->config->get('storage', 'backends'));
277                 self::assertEquals(StorageManager::DEFAULT_BACKENDS, $storageManager->listBackends());
278         }
279
280         /**
281          * tests that an active backend cannot get unregistered
282          */
283         public function testUnregisterActiveBackend()
284         {
285                 /// @todo Remove dice once "Hook" is dynamic and mockable
286                 $dice = (new Dice())
287                         ->addRules(include __DIR__ . '/../../../../../static/dependencies.config.php')
288                         ->addRule(Database::class, ['instanceOf' => StaticDatabase::class, 'shared' => true])
289                         ->addRule(IHandleSessions::class, ['instanceOf' => Memory::class, 'shared' => true, 'call' => null]);
290                 DI::init($dice);
291
292                 $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n);
293
294                 self::assertTrue($storageManager->register(SampleStorageBackend::class));
295
296                 self::assertEquals(array_merge(StorageManager::DEFAULT_BACKENDS, [
297                         SampleStorageBackend::getName(),
298                 ]), $storageManager->listBackends());
299                 self::assertEquals(array_merge(StorageManager::DEFAULT_BACKENDS, [
300                         SampleStorageBackend::getName()
301                 ]), $this->config->get('storage', 'backends'));
302
303                 // inline call to register own class as hook (testing purpose only)
304                 SampleStorageBackend::registerHook();
305                 Hook::loadHooks();
306
307                 self::assertTrue($storageManager->setBackend($storageManager->getWritableStorageByName(SampleStorageBackend::NAME)));
308                 self::assertEquals(SampleStorageBackend::NAME, $this->config->get('storage', 'name'));
309
310                 self::assertInstanceOf(SampleStorageBackend::class, $storageManager->getBackend());
311
312                 self::expectException(StorageException::class);
313                 self::expectExceptionMessage('Cannot unregister Sample Storage, because it\'s currently active.');
314
315                 $storageManager->unregister(SampleStorageBackend::class);
316         }
317
318         /**
319          * Test moving data to a new storage (currently testing db & filesystem)
320          *
321          * @dataProvider dataStorages
322          */
323         public function testMoveStorage($name, $valid, $interface, $assert, $assertName)
324         {
325                 if ($interface !== ICanWriteToStorage::class) {
326                         self::markTestSkipped("No user backend");
327                 }
328
329                 $this->loadFixture(__DIR__ . '/../../../../datasets/storage/database.fixture.php', $this->dba);
330
331                 $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n);
332                 $storage        = $storageManager->getWritableStorageByName($name);
333                 $storageManager->move($storage);
334
335                 $photos = $this->dba->select('photo', ['backend-ref', 'backend-class', 'id', 'data']);
336
337                 while ($photo = $this->dba->fetch($photos)) {
338                         self::assertEmpty($photo['data']);
339
340                         $storage = $storageManager->getByName($photo['backend-class']);
341                         $data    = $storage->get($photo['backend-ref']);
342
343                         self::assertNotEmpty($data);
344                 }
345         }
346
347         /**
348          * Test moving data to a WRONG storage
349          */
350         public function testWrongWritableStorage()
351         {
352                 $this->expectException(InvalidClassStorageException::class);
353                 $this->expectExceptionMessage('Backend SystemResource is not valid');
354
355                 $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n);
356                 $storage        = $storageManager->getWritableStorageByName(SystemResource::getName());
357                 $storageManager->move($storage);
358         }
359 }