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