Completed
Push — master ( 158b3e...c62fa5 )
by Joas
29:53 queued 14s
created
tests/lib/Command/BackgroundModeTest.php 1 patch
Indentation   +30 added lines, -30 removed lines patch added patch discarded remove patch
@@ -20,43 +20,43 @@
 block discarded – undo
20 20
  * @group DB
21 21
  */
22 22
 class BackgroundModeTest extends TestCase {
23
-	private IAppConfig $appConfig;
23
+    private IAppConfig $appConfig;
24 24
 
25
-	private Mode $command;
25
+    private Mode $command;
26 26
 
27
-	public function setUp(): void {
28
-		$this->appConfig = $this->createMock(IAppConfig::class);
27
+    public function setUp(): void {
28
+        $this->appConfig = $this->createMock(IAppConfig::class);
29 29
 
30
-		$inputDefinition = new InputDefinition([
31
-			new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
32
-		]);
30
+        $inputDefinition = new InputDefinition([
31
+            new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
32
+        ]);
33 33
 
34
-		$this->command = new Mode($this->appConfig);
35
-		$this->command->setDefinition($inputDefinition);
36
-	}
34
+        $this->command = new Mode($this->appConfig);
35
+        $this->command->setDefinition($inputDefinition);
36
+    }
37 37
 
38
-	/**
39
-	 * @dataProvider dataModeCommand
40
-	 */
41
-	public function testModeCommand(string $mode): void {
42
-		$this->appConfig->expects($this->once())
43
-			->method('setValueString')
44
-			->with('core', 'backgroundjobs_mode', $mode);
38
+    /**
39
+     * @dataProvider dataModeCommand
40
+     */
41
+    public function testModeCommand(string $mode): void {
42
+        $this->appConfig->expects($this->once())
43
+            ->method('setValueString')
44
+            ->with('core', 'backgroundjobs_mode', $mode);
45 45
 
46
-		$commandTester = new CommandTester($this->command);
47
-		$commandTester->execute(['command' => 'background:' . $mode]);
46
+        $commandTester = new CommandTester($this->command);
47
+        $commandTester->execute(['command' => 'background:' . $mode]);
48 48
 
49
-		$commandTester->assertCommandIsSuccessful();
49
+        $commandTester->assertCommandIsSuccessful();
50 50
 
51
-		$output = $commandTester->getDisplay();
52
-		$this->assertStringContainsString($mode, $output);
53
-	}
51
+        $output = $commandTester->getDisplay();
52
+        $this->assertStringContainsString($mode, $output);
53
+    }
54 54
 
55
-	public static function dataModeCommand(): array {
56
-		return [
57
-			'ajax' => ['ajax'],
58
-			'cron' => ['cron'],
59
-			'webcron' => ['webcron'],
60
-		];
61
-	}
55
+    public static function dataModeCommand(): array {
56
+        return [
57
+            'ajax' => ['ajax'],
58
+            'cron' => ['cron'],
59
+            'webcron' => ['webcron'],
60
+        ];
61
+    }
62 62
 }
Please login to merge, or discard this patch.
tests/lib/Command/CronBusTest.php 1 patch
Indentation   +17 added lines, -17 removed lines patch added patch discarded remove patch
@@ -13,26 +13,26 @@
 block discarded – undo
13 13
  * @group DB
14 14
  */
15 15
 class CronBusTest extends AsyncBusTestCase {
16
-	/**
17
-	 * @var \OCP\BackgroundJob\IJobList
18
-	 */
19
-	private $jobList;
16
+    /**
17
+     * @var \OCP\BackgroundJob\IJobList
18
+     */
19
+    private $jobList;
20 20
 
21 21
 
22
-	protected function setUp(): void {
23
-		parent::setUp();
22
+    protected function setUp(): void {
23
+        parent::setUp();
24 24
 
25
-		$this->jobList = new DummyJobList();
26
-	}
25
+        $this->jobList = new DummyJobList();
26
+    }
27 27
 
28
-	protected function createBus() {
29
-		return new CronBus($this->jobList);
30
-	}
28
+    protected function createBus() {
29
+        return new CronBus($this->jobList);
30
+    }
31 31
 
32
-	protected function runJobs() {
33
-		$jobs = $this->jobList->getAll();
34
-		foreach ($jobs as $job) {
35
-			$job->start($this->jobList);
36
-		}
37
-	}
32
+    protected function runJobs() {
33
+        $jobs = $this->jobList->getAll();
34
+        foreach ($jobs as $job) {
35
+            $job->start($this->jobList);
36
+        }
37
+    }
38 38
 }
Please login to merge, or discard this patch.
tests/lib/Config/UserConfigTest.php 1 patch
Indentation   +1802 added lines, -1802 removed lines patch added patch discarded remove patch
@@ -26,1806 +26,1806 @@
 block discarded – undo
26 26
  * @package Test
27 27
  */
28 28
 class UserConfigTest extends TestCase {
29
-	protected IDBConnection $connection;
30
-	private IConfig $config;
31
-	private LoggerInterface $logger;
32
-	private ICrypto $crypto;
33
-	private array $originalPreferences;
34
-
35
-	/**
36
-	 * @var array<string, array<string, array<array<string, string, int, bool, bool>>> [userId => [appId => prefKey, prefValue, valueType, lazy, sensitive]]]
37
-	 */
38
-	private array $basePreferences =
39
-		[
40
-			'user1' =>
41
-				[
42
-					'app1' => [
43
-						'key1' => ['key1', 'value1'],
44
-						'key22' => ['key22', '31'],
45
-						'fast_string' => ['fast_string', 'f_value', ValueType::STRING],
46
-						'lazy_string' => ['lazy_string', 'l_value', ValueType::STRING, true],
47
-						'fast_string_sensitive' => [
48
-							'fast_string_sensitive', 'fs_value', ValueType::STRING, false, UserConfig::FLAG_SENSITIVE
49
-						],
50
-						'lazy_string_sensitive' => [
51
-							'lazy_string_sensitive', 'ls_value', ValueType::STRING, true, UserConfig::FLAG_SENSITIVE
52
-						],
53
-						'fast_int' => ['fast_int', 11, ValueType::INT],
54
-						'lazy_int' => ['lazy_int', 12, ValueType::INT, true],
55
-						'fast_int_sensitive' => ['fast_int_sensitive', 2024, ValueType::INT, false, UserConfig::FLAG_SENSITIVE],
56
-						'lazy_int_sensitive' => ['lazy_int_sensitive', 2048, ValueType::INT, true, UserConfig::FLAG_SENSITIVE],
57
-						'fast_float' => ['fast_float', 3.14, ValueType::FLOAT],
58
-						'lazy_float' => ['lazy_float', 3.14159, ValueType::FLOAT, true],
59
-						'fast_float_sensitive' => [
60
-							'fast_float_sensitive', 1.41, ValueType::FLOAT, false, UserConfig::FLAG_SENSITIVE
61
-						],
62
-						'lazy_float_sensitive' => [
63
-							'lazy_float_sensitive', 1.4142, ValueType::FLOAT, true, UserConfig::FLAG_SENSITIVE
64
-						],
65
-						'fast_array' => ['fast_array', ['year' => 2024], ValueType::ARRAY],
66
-						'lazy_array' => ['lazy_array', ['month' => 'October'], ValueType::ARRAY, true],
67
-						'fast_array_sensitive' => [
68
-							'fast_array_sensitive', ['password' => 'pwd'], ValueType::ARRAY, false, UserConfig::FLAG_SENSITIVE
69
-						],
70
-						'lazy_array_sensitive' => [
71
-							'lazy_array_sensitive', ['password' => 'qwerty'], ValueType::ARRAY, true, UserConfig::FLAG_SENSITIVE
72
-						],
73
-						'fast_boolean' => ['fast_boolean', true, ValueType::BOOL],
74
-						'fast_boolean_0' => ['fast_boolean_0', false, ValueType::BOOL],
75
-						'lazy_boolean' => ['lazy_boolean', true, ValueType::BOOL, true],
76
-						'lazy_boolean_0' => ['lazy_boolean_0', false, ValueType::BOOL, true],
77
-					],
78
-					'app2' => [
79
-						'key2' => ['key2', 'value2a', ValueType::STRING, false, 0, true],
80
-						'key3' => ['key3', 'value3', ValueType::STRING, true],
81
-						'key4' => ['key4', 'value4', ValueType::STRING, false, UserConfig::FLAG_SENSITIVE],
82
-						'key8' => ['key8', 11, ValueType::INT, false, 0, true],
83
-						'key9' => ['key9', 'value9a', ValueType::STRING],
84
-					],
85
-					'app3' => [
86
-						'key1' => ['key1', 'value123'],
87
-						'key3' => ['key3', 'value3'],
88
-						'key8' => ['key8', 12, ValueType::INT, false, UserConfig::FLAG_SENSITIVE, true],
89
-						'key9' => ['key9', 'value9b', ValueType::STRING, false, UserConfig::FLAG_SENSITIVE],
90
-						'key10' => ['key10', true, ValueType::BOOL, false, 0, true],
91
-					],
92
-					'only-lazy' => [
93
-						'key1' => ['key1', 'value456', ValueType::STRING, true, 0, true],
94
-						'key2' => ['key2', 'value2c', ValueType::STRING, true, UserConfig::FLAG_SENSITIVE],
95
-						'key3' => ['key3', 42, ValueType::INT, true],
96
-						'key4' => ['key4', 17.42, ValueType::FLOAT, true],
97
-						'key5' => ['key5', true, ValueType::BOOL, true],
98
-					]
99
-				],
100
-			'user2' =>
101
-				[
102
-					'app1' => [
103
-						'1' => ['1', 'value1'],
104
-						'2' => ['2', 'value2', ValueType::STRING, true, UserConfig::FLAG_SENSITIVE],
105
-						'3' => ['3', 17, ValueType::INT, true],
106
-						'4' => ['4', 42, ValueType::INT, false, UserConfig::FLAG_SENSITIVE],
107
-						'5' => ['5', 17.42, ValueType::FLOAT, false],
108
-						'6' => ['6', true, ValueType::BOOL, false],
109
-					],
110
-					'app2' => [
111
-						'key2' => ['key2', 'value2b', ValueType::STRING, false, 0, true],
112
-						'key3' => ['key3', 'value3', ValueType::STRING, true],
113
-						'key4' => ['key4', 'value4', ValueType::STRING, false, UserConfig::FLAG_SENSITIVE],
114
-						'key8' => ['key8', 12, ValueType::INT, false, 0, true],
115
-					],
116
-					'app3' => [
117
-						'key10' => ['key10', false, ValueType::BOOL, false, 0, true],
118
-					],
119
-					'only-lazy' => [
120
-						'key1' => ['key1', 'value1', ValueType::STRING, true, 0, true]
121
-					]
122
-				],
123
-			'user3' =>
124
-				[
125
-					'app2' => [
126
-						'key2' => ['key2', 'value2c', ValueType::MIXED, false, 0, true],
127
-						'key3' => ['key3', 'value3', ValueType::STRING, true, ],
128
-						'key4' => ['key4', 'value4', ValueType::STRING, false, UserConfig::FLAG_SENSITIVE],
129
-						'fast_string_sensitive' => [
130
-							'fast_string_sensitive', 'fs_value', ValueType::STRING, false, UserConfig::FLAG_SENSITIVE
131
-						],
132
-						'lazy_string_sensitive' => [
133
-							'lazy_string_sensitive', 'ls_value', ValueType::STRING, true, UserConfig::FLAG_SENSITIVE
134
-						],
135
-					],
136
-					'only-lazy' => [
137
-						'key3' => ['key3', 'value3', ValueType::STRING, true]
138
-					]
139
-				],
140
-			'user4' =>
141
-				[
142
-					'app2' => [
143
-						'key1' => ['key1', 'value1'],
144
-						'key2' => ['key2', 'value2A', ValueType::MIXED, false, 0, true],
145
-						'key3' => ['key3', 'value3', ValueType::STRING, true,],
146
-						'key4' => ['key4', 'value4', ValueType::STRING, false, UserConfig::FLAG_SENSITIVE],
147
-					],
148
-					'app3' => [
149
-						'key10' => ['key10', true, ValueType::BOOL, false, 0, true],
150
-					],
151
-					'only-lazy' => [
152
-						'key1' => ['key1', 123, ValueType::INT, true, 0, true]
153
-					]
154
-				],
155
-			'user5' =>
156
-				[
157
-					'app1' => [
158
-						'key1' => ['key1', 'value1']
159
-					],
160
-					'app2' => [
161
-						'key8' => ['key8', 12, ValueType::INT, false, 0, true]
162
-					],
163
-					'only-lazy' => [
164
-						'key1' => ['key1', 'value1', ValueType::STRING, true, 0, true]
165
-					]
166
-				],
167
-
168
-		];
169
-
170
-	protected function setUp(): void {
171
-		parent::setUp();
172
-
173
-		$this->connection = \OCP\Server::get(IDBConnection::class);
174
-		$this->config = \OCP\Server::get(IConfig::class);
175
-		$this->logger = \OCP\Server::get(LoggerInterface::class);
176
-		$this->crypto = \OCP\Server::get(ICrypto::class);
177
-
178
-		// storing current preferences and emptying the data table
179
-		$sql = $this->connection->getQueryBuilder();
180
-		$sql->select('*')
181
-			->from('preferences');
182
-		$result = $sql->executeQuery();
183
-		$this->originalPreferences = $result->fetchAll();
184
-		$result->closeCursor();
185
-
186
-		$sql = $this->connection->getQueryBuilder();
187
-		$sql->delete('preferences');
188
-		$sql->executeStatement();
189
-
190
-		$sql = $this->connection->getQueryBuilder();
191
-		$sql->insert('preferences')
192
-			->values(
193
-				[
194
-					'userid' => $sql->createParameter('userid'),
195
-					'appid' => $sql->createParameter('appid'),
196
-					'configkey' => $sql->createParameter('configkey'),
197
-					'configvalue' => $sql->createParameter('configvalue'),
198
-					'type' => $sql->createParameter('type'),
199
-					'lazy' => $sql->createParameter('lazy'),
200
-					'flags' => $sql->createParameter('flags'),
201
-					'indexed' => $sql->createParameter('indexed')
202
-				]
203
-			);
204
-
205
-		foreach ($this->basePreferences as $userId => $userData) {
206
-			foreach ($userData as $appId => $appData) {
207
-				foreach ($appData as $key => $row) {
208
-					$value = $row[1];
209
-					$type = ($row[2] ?? ValueType::MIXED)->value;
210
-
211
-					if ($type === ValueType::ARRAY->value) {
212
-						$value = json_encode($value);
213
-					}
214
-
215
-					if ($type === ValueType::BOOL->value && $value === false) {
216
-						$value = '0';
217
-					}
218
-
219
-					$flags = $row[4] ?? 0;
220
-					if ((UserConfig::FLAG_SENSITIVE & $flags) !== 0) {
221
-						$value = self::invokePrivate(UserConfig::class, 'ENCRYPTION_PREFIX')
222
-								 . $this->crypto->encrypt((string)$value);
223
-					} else {
224
-						$indexed = (($row[5] ?? false) === true) ? $value : '';
225
-					}
226
-
227
-					$sql->setParameters(
228
-						[
229
-							'userid' => $userId,
230
-							'appid' => $appId,
231
-							'configkey' => $row[0],
232
-							'configvalue' => $value,
233
-							'type' => $type,
234
-							'lazy' => (($row[3] ?? false) === true) ? 1 : 0,
235
-							'flags' => $flags,
236
-							'indexed' => $indexed ?? ''
237
-						]
238
-					)->executeStatement();
239
-				}
240
-			}
241
-		}
242
-	}
243
-
244
-	protected function tearDown(): void {
245
-		$sql = $this->connection->getQueryBuilder();
246
-		$sql->delete('preferences');
247
-		$sql->executeStatement();
248
-
249
-		$sql = $this->connection->getQueryBuilder();
250
-		$sql->insert('preferences')
251
-			->values(
252
-				[
253
-					'userid' => $sql->createParameter('userid'),
254
-					'appid' => $sql->createParameter('appid'),
255
-					'configkey' => $sql->createParameter('configkey'),
256
-					'configvalue' => $sql->createParameter('configvalue'),
257
-					'lazy' => $sql->createParameter('lazy'),
258
-					'type' => $sql->createParameter('type'),
259
-				]
260
-			);
261
-
262
-		foreach ($this->originalPreferences as $key => $configs) {
263
-			$sql->setParameter('userid', $configs['userid'])
264
-				->setParameter('appid', $configs['appid'])
265
-				->setParameter('configkey', $configs['configkey'])
266
-				->setParameter('configvalue', $configs['configvalue'])
267
-				->setParameter('lazy', ($configs['lazy'] === '1') ? '1' : '0')
268
-				->setParameter('type', $configs['type']);
269
-			$sql->executeStatement();
270
-		}
271
-
272
-		parent::tearDown();
273
-	}
274
-
275
-	/**
276
-	 * @param array $preLoading preload the 'fast' cache for a list of users)
277
-	 *
278
-	 * @return IUserConfig
279
-	 */
280
-	private function generateUserConfig(array $preLoading = []): IUserConfig {
281
-		$userConfig = new \OC\Config\UserConfig(
282
-			$this->connection,
283
-			$this->config,
284
-			$this->logger,
285
-			$this->crypto,
286
-		);
287
-		$msg = ' generateUserConfig() failed to confirm cache status';
288
-
289
-		// confirm cache status
290
-		$status = $userConfig->statusCache();
291
-		$this->assertSame([], $status['fastLoaded'], $msg);
292
-		$this->assertSame([], $status['lazyLoaded'], $msg);
293
-		$this->assertSame([], $status['fastCache'], $msg);
294
-		$this->assertSame([], $status['lazyCache'], $msg);
295
-		foreach ($preLoading as $preLoadUser) {
296
-			// simple way to initiate the load of non-lazy preferences values in cache
297
-			$userConfig->getValueString($preLoadUser, 'core', 'preload');
298
-
299
-			// confirm cache status
300
-			$status = $userConfig->statusCache();
301
-			$this->assertSame(true, $status['fastLoaded'][$preLoadUser], $msg);
302
-			$this->assertSame(false, $status['lazyLoaded'][$preLoadUser], $msg);
303
-
304
-			$apps = array_values(array_diff(array_keys($this->basePreferences[$preLoadUser]), ['only-lazy']));
305
-			$this->assertEqualsCanonicalizing($apps, array_keys($status['fastCache'][$preLoadUser]), $msg);
306
-			$this->assertSame([], array_keys($status['lazyCache'][$preLoadUser]), $msg);
307
-		}
308
-
309
-		return $userConfig;
310
-	}
311
-
312
-	public function testGetUserIdsEmpty(): void {
313
-		$userConfig = $this->generateUserConfig();
314
-		$this->assertEqualsCanonicalizing(array_keys($this->basePreferences), $userConfig->getUserIds());
315
-	}
316
-
317
-	public function testGetUserIds(): void {
318
-		$userConfig = $this->generateUserConfig();
319
-		$this->assertEqualsCanonicalizing(['user1', 'user2', 'user5'], $userConfig->getUserIds('app1'));
320
-	}
321
-
322
-	public function testGetApps(): void {
323
-		$userConfig = $this->generateUserConfig();
324
-		$this->assertEqualsCanonicalizing(
325
-			array_keys($this->basePreferences['user1']), $userConfig->getApps('user1')
326
-		);
327
-	}
328
-
329
-	public function testGetKeys(): void {
330
-		$userConfig = $this->generateUserConfig(['user1']);
331
-		$this->assertEqualsCanonicalizing(
332
-			array_keys($this->basePreferences['user1']['app1']), $userConfig->getKeys('user1', 'app1')
333
-		);
334
-	}
335
-
336
-	public static function providerHasKey(): array {
337
-		return [
338
-			['user1', 'app1', 'key1', false, true],
339
-			['user0', 'app1', 'key1', false, false],
340
-			['user1', 'app1', 'key1', true, false],
341
-			['user1', 'app1', 'key0', false, false],
342
-			['user1', 'app1', 'key0', true, false],
343
-			['user1', 'app1', 'fast_string_sensitive', false, true],
344
-			['user1', 'app1', 'lazy_string_sensitive', true, true],
345
-			['user2', 'only-lazy', 'key1', false, false],
346
-			['user2', 'only-lazy', 'key1', true, true],
347
-		];
348
-	}
349
-
350
-	/**
351
-	 * @dataProvider providerHasKey
352
-	 */
353
-	public function testHasKey(string $userId, string $appId, string $key, ?bool $lazy, bool $result): void {
354
-		$userConfig = $this->generateUserConfig();
355
-		$this->assertEquals($result, $userConfig->hasKey($userId, $appId, $key, $lazy));
356
-	}
357
-
358
-	public static function providerIsSensitive(): array {
359
-		return [
360
-			['user1', 'app1', 'key1', false, false, false],
361
-			['user0', 'app1', 'key1', false, false, true],
362
-			['user1', 'app1', 'key1', true, false, true],
363
-			['user1', 'app1', 'key1', null, false, false],
364
-			['user1', 'app1', 'key0', false, false, true],
365
-			['user1', 'app1', 'key0', true, false, true],
366
-			['user1', 'app1', 'fast_string_sensitive', false, true, false],
367
-			['user1', 'app1', 'lazy_string_sensitive', true, true, false],
368
-			['user1', 'app1', 'fast_string_sensitive', true, true, true],
369
-			['user1', 'app1', 'lazy_string_sensitive', false, true, true],
370
-			['user1', 'app1', 'lazy_string_sensitive', null, true, false],
371
-			['user2', 'only-lazy', 'key1', false, false, true],
372
-			['user2', 'only-lazy', 'key1', true, false, false],
373
-			['user2', 'only-lazy', 'key1', null, false, false],
374
-		];
375
-	}
376
-
377
-	/**
378
-	 * @dataProvider providerIsSensitive
379
-	 */
380
-	public function testIsSensitive(
381
-		string $userId,
382
-		string $appId,
383
-		string $key,
384
-		?bool $lazy,
385
-		bool $result,
386
-		bool $exception,
387
-	): void {
388
-		$userConfig = $this->generateUserConfig();
389
-		if ($exception) {
390
-			$this->expectException(UnknownKeyException::class);
391
-		}
392
-
393
-		$this->assertEquals($result, $userConfig->isSensitive($userId, $appId, $key, $lazy));
394
-	}
395
-
396
-	public static function providerIsLazy(): array {
397
-		return [
398
-			['user1', 'app1', 'key1', false, false],
399
-			['user0', 'app1', 'key1', false, true],
400
-			['user1', 'app1', 'key0', false, true],
401
-			['user1', 'app1', 'key0', false, true],
402
-			['user1', 'app1', 'fast_string_sensitive', false, false],
403
-			['user1', 'app1', 'lazy_string_sensitive', true, false],
404
-			['user2', 'only-lazy', 'key1', true, false],
405
-		];
406
-	}
407
-
408
-	/**
409
-	 * @dataProvider providerIsLazy
410
-	 */
411
-	public function testIsLazy(
412
-		string $userId,
413
-		string $appId,
414
-		string $key,
415
-		bool $result,
416
-		bool $exception,
417
-	): void {
418
-		$userConfig = $this->generateUserConfig();
419
-		if ($exception) {
420
-			$this->expectException(UnknownKeyException::class);
421
-		}
422
-
423
-		$this->assertEquals($result, $userConfig->isLazy($userId, $appId, $key));
424
-	}
425
-
426
-	public static function providerGetValues(): array {
427
-		return [
428
-			[
429
-				'user1', 'app1', '', true,
430
-				[
431
-					'fast_array' => ['year' => 2024],
432
-					'fast_array_sensitive' => '***REMOVED SENSITIVE VALUE***',
433
-					'fast_boolean' => true,
434
-					'fast_boolean_0' => false,
435
-					'fast_float' => 3.14,
436
-					'fast_float_sensitive' => '***REMOVED SENSITIVE VALUE***',
437
-					'fast_int' => 11,
438
-					'fast_int_sensitive' => '***REMOVED SENSITIVE VALUE***',
439
-					'fast_string' => 'f_value',
440
-					'fast_string_sensitive' => '***REMOVED SENSITIVE VALUE***',
441
-					'key1' => 'value1',
442
-					'key22' => '31',
443
-					'lazy_array' => ['month' => 'October'],
444
-					'lazy_array_sensitive' => '***REMOVED SENSITIVE VALUE***',
445
-					'lazy_boolean' => true,
446
-					'lazy_boolean_0' => false,
447
-					'lazy_float' => 3.14159,
448
-					'lazy_float_sensitive' => '***REMOVED SENSITIVE VALUE***',
449
-					'lazy_int' => 12,
450
-					'lazy_int_sensitive' => '***REMOVED SENSITIVE VALUE***',
451
-					'lazy_string' => 'l_value',
452
-					'lazy_string_sensitive' => '***REMOVED SENSITIVE VALUE***',
453
-				]
454
-			],
455
-			[
456
-				'user1', 'app1', '', false,
457
-				[
458
-					'fast_array' => ['year' => 2024],
459
-					'fast_array_sensitive' => ['password' => 'pwd'],
460
-					'fast_boolean' => true,
461
-					'fast_boolean_0' => false,
462
-					'fast_float' => 3.14,
463
-					'fast_float_sensitive' => 1.41,
464
-					'fast_int' => 11,
465
-					'fast_int_sensitive' => 2024,
466
-					'fast_string' => 'f_value',
467
-					'fast_string_sensitive' => 'fs_value',
468
-					'key1' => 'value1',
469
-					'key22' => '31',
470
-					'lazy_array' => ['month' => 'October'],
471
-					'lazy_array_sensitive' => ['password' => 'qwerty'],
472
-					'lazy_boolean' => true,
473
-					'lazy_boolean_0' => false,
474
-					'lazy_float' => 3.14159,
475
-					'lazy_float_sensitive' => 1.4142,
476
-					'lazy_int' => 12,
477
-					'lazy_int_sensitive' => 2048,
478
-					'lazy_string' => 'l_value',
479
-					'lazy_string_sensitive' => 'ls_value'
480
-				]
481
-			],
482
-			[
483
-				'user1', 'app1', 'fast_', true,
484
-				[
485
-					'fast_array' => ['year' => 2024],
486
-					'fast_array_sensitive' => '***REMOVED SENSITIVE VALUE***',
487
-					'fast_boolean' => true,
488
-					'fast_boolean_0' => false,
489
-					'fast_float' => 3.14,
490
-					'fast_float_sensitive' => '***REMOVED SENSITIVE VALUE***',
491
-					'fast_int' => 11,
492
-					'fast_int_sensitive' => '***REMOVED SENSITIVE VALUE***',
493
-					'fast_string' => 'f_value',
494
-					'fast_string_sensitive' => '***REMOVED SENSITIVE VALUE***',
495
-				]
496
-			],
497
-			[
498
-				'user1', 'app1', 'fast_', false,
499
-				[
500
-					'fast_array' => ['year' => 2024],
501
-					'fast_array_sensitive' => ['password' => 'pwd'],
502
-					'fast_boolean' => true,
503
-					'fast_boolean_0' => false,
504
-					'fast_float' => 3.14,
505
-					'fast_float_sensitive' => 1.41,
506
-					'fast_int' => 11,
507
-					'fast_int_sensitive' => 2024,
508
-					'fast_string' => 'f_value',
509
-					'fast_string_sensitive' => 'fs_value',
510
-				]
511
-			],
512
-			[
513
-				'user1', 'app1', 'key1', true,
514
-				[
515
-					'key1' => 'value1',
516
-				]
517
-			],
518
-			[
519
-				'user2', 'app1', '', false,
520
-				[
521
-					'1' => 'value1',
522
-					'4' => 42,
523
-					'5' => 17.42,
524
-					'6' => true,
525
-					'2' => 'value2',
526
-					'3' => 17,
527
-				]
528
-			],
529
-			[
530
-				'user2', 'app1', '', true,
531
-				[
532
-					'1' => 'value1',
533
-					'4' => '***REMOVED SENSITIVE VALUE***',
534
-					'5' => 17.42,
535
-					'6' => true,
536
-					'2' => '***REMOVED SENSITIVE VALUE***',
537
-					'3' => 17,
538
-				]
539
-			],
540
-		];
541
-	}
542
-
543
-	/**
544
-	 * @dataProvider providerGetValues
545
-	 */
546
-	public function testGetValues(
547
-		string $userId,
548
-		string $appId,
549
-		string $prefix,
550
-		bool $filtered,
551
-		array $result,
552
-	): void {
553
-		$userConfig = $this->generateUserConfig();
554
-		$this->assertJsonStringEqualsJsonString(
555
-			json_encode($result), json_encode($userConfig->getValues($userId, $appId, $prefix, $filtered))
556
-		);
557
-	}
558
-
559
-	public static function providerGetAllValues(): array {
560
-		return [
561
-			[
562
-				'user2', false,
563
-				[
564
-					'app1' => [
565
-						'1' => 'value1',
566
-						'4' => 42,
567
-						'5' => 17.42,
568
-						'6' => true,
569
-						'2' => 'value2',
570
-						'3' => 17,
571
-					],
572
-					'app2' => [
573
-						'key2' => 'value2b',
574
-						'key3' => 'value3',
575
-						'key4' => 'value4',
576
-						'key8' => 12,
577
-					],
578
-					'app3' => [
579
-						'key10' => false,
580
-					],
581
-					'only-lazy' => [
582
-						'key1' => 'value1',
583
-					]
584
-				],
585
-			],
586
-			[
587
-				'user2', true,
588
-				[
589
-					'app1' => [
590
-						'1' => 'value1',
591
-						'4' => '***REMOVED SENSITIVE VALUE***',
592
-						'5' => 17.42,
593
-						'6' => true,
594
-						'2' => '***REMOVED SENSITIVE VALUE***',
595
-						'3' => 17,
596
-					],
597
-					'app2' => [
598
-						'key2' => 'value2b',
599
-						'key3' => 'value3',
600
-						'key4' => '***REMOVED SENSITIVE VALUE***',
601
-						'key8' => 12,
602
-					],
603
-					'app3' => [
604
-						'key10' => false,
605
-					],
606
-					'only-lazy' => [
607
-						'key1' => 'value1',
608
-					]
609
-				],
610
-			],
611
-			[
612
-				'user3', true,
613
-				[
614
-					'app2' => [
615
-						'key2' => 'value2c',
616
-						'key3' => 'value3',
617
-						'key4' => '***REMOVED SENSITIVE VALUE***',
618
-						'fast_string_sensitive' => '***REMOVED SENSITIVE VALUE***',
619
-						'lazy_string_sensitive' => '***REMOVED SENSITIVE VALUE***',
620
-					],
621
-					'only-lazy' => [
622
-						'key3' => 'value3',
623
-					]
624
-				],
625
-			],
626
-			[
627
-				'user3', false,
628
-				[
629
-					'app2' => [
630
-						'key2' => 'value2c',
631
-						'key3' => 'value3',
632
-						'key4' => 'value4',
633
-						'fast_string_sensitive' => 'fs_value',
634
-						'lazy_string_sensitive' => 'ls_value',
635
-					],
636
-					'only-lazy' => [
637
-						'key3' => 'value3',
638
-					]
639
-				],
640
-			],
641
-		];
642
-	}
643
-
644
-	/**
645
-	 * @dataProvider providerGetAllValues
646
-	 */
647
-	public function testGetAllValues(
648
-		string $userId,
649
-		bool $filtered,
650
-		array $result,
651
-	): void {
652
-		$userConfig = $this->generateUserConfig();
653
-		$this->assertEqualsCanonicalizing($result, $userConfig->getAllValues($userId, $filtered));
654
-	}
655
-
656
-	public static function providerSearchValuesByApps(): array {
657
-		return [
658
-			[
659
-				'user1', 'key1', false, null,
660
-				[
661
-					'app1' => 'value1',
662
-					'app3' => 'value123'
663
-				]
664
-			],
665
-			[
666
-				'user1', 'key1', true, null,
667
-				[
668
-					'only-lazy' => 'value456'
669
-				]
670
-			],
671
-			[
672
-				'user1', 'key8', false, null,
673
-				[
674
-					'app2' => 11,
675
-					'app3' => 12,
676
-				]
677
-			],
678
-			[
679
-				'user1', 'key9', false, ValueType::INT,
680
-				[
681
-					'app2' => 0,
682
-					'app3' => 0,
683
-				]
684
-			]
685
-		];
686
-	}
687
-
688
-	/**
689
-	 * @dataProvider providerSearchValuesByApps
690
-	 */
691
-	public function testSearchValuesByApps(
692
-		string $userId,
693
-		string $key,
694
-		bool $lazy,
695
-		?ValueType $typedAs,
696
-		array $result,
697
-	): void {
698
-		$userConfig = $this->generateUserConfig();
699
-		$this->assertEquals($result, $userConfig->getValuesByApps($userId, $key, $lazy, $typedAs));
700
-	}
701
-
702
-	public static function providerSearchValuesByUsers(): array {
703
-		return [
704
-			[
705
-				'app2', 'key2', null, null,
706
-				[
707
-					'user1' => 'value2a',
708
-					'user2' => 'value2b',
709
-					'user3' => 'value2c',
710
-					'user4' => 'value2A'
711
-				]
712
-			],
713
-			[
714
-				'app2', 'key2', null, ['user1', 'user3'],
715
-				[
716
-					'user1' => 'value2a',
717
-					'user3' => 'value2c',
718
-				]
719
-			],
720
-			[
721
-				'app2', 'key2', ValueType::INT, ['user1', 'user3'],
722
-				[
723
-					'user1' => 0,
724
-					'user3' => 0,
725
-				]
726
-			],
727
-			[
728
-				'app2', 'key8', ValueType::INT, null,
729
-				[
730
-					'user1' => 11,
731
-					'user2' => 12,
732
-					'user5' => 12,
733
-				]
734
-			],
735
-		];
736
-	}
737
-
738
-	/**
739
-	 * @dataProvider providerSearchValuesByUsers
740
-	 */
741
-	public function testSearchValuesByUsers(
742
-		string $app,
743
-		string $key,
744
-		?ValueType $typedAs,
745
-		?array $userIds,
746
-		array $result,
747
-	): void {
748
-		$userConfig = $this->generateUserConfig();
749
-		$this->assertEqualsCanonicalizing(
750
-			$result, $userConfig->getValuesByUsers($app, $key, $typedAs, $userIds)
751
-		);
752
-	}
753
-
754
-	public static function providerSearchValuesByValueString(): array {
755
-		return [
756
-			['app2', 'key2', 'value2a', false, ['user1']],
757
-			['app2', 'key2', 'value2A', false, ['user4']],
758
-			['app2', 'key2', 'value2A', true, ['user1', 'user4']]
759
-		];
760
-	}
761
-
762
-	/**
763
-	 * @dataProvider providerSearchValuesByValueString
764
-	 */
765
-	public function testSearchUsersByValueString(
766
-		string $app,
767
-		string $key,
768
-		string|array $value,
769
-		bool $ci,
770
-		array $result,
771
-	): void {
772
-		$userConfig = $this->generateUserConfig();
773
-		$this->assertEqualsCanonicalizing($result, iterator_to_array($userConfig->searchUsersByValueString($app, $key, $value, $ci)));
774
-	}
775
-
776
-	public static function providerSearchValuesByValueInt(): array {
777
-		return [
778
-			['app3', 'key8', 12, []], // sensitive value, cannot search
779
-			['app2', 'key8', 12, ['user2', 'user5']], // sensitive value, cannot search
780
-			['only-lazy', 'key1', 123, ['user4']],
781
-		];
782
-	}
783
-
784
-	/**
785
-	 * @dataProvider providerSearchValuesByValueInt
786
-	 */
787
-	public function testSearchUsersByValueInt(
788
-		string $app,
789
-		string $key,
790
-		int $value,
791
-		array $result,
792
-	): void {
793
-		$userConfig = $this->generateUserConfig();
794
-		$this->assertEqualsCanonicalizing($result, iterator_to_array($userConfig->searchUsersByValueInt($app, $key, $value)));
795
-	}
796
-
797
-	public static function providerSearchValuesByValues(): array {
798
-		return [
799
-			['app2', 'key2', ['value2a', 'value2b'], ['user1', 'user2']],
800
-			['app2', 'key2', ['value2a', 'value2c'], ['user1', 'user3']],
801
-		];
802
-	}
803
-
804
-	/**
805
-	 * @dataProvider providerSearchValuesByValues
806
-	 */
807
-	public function testSearchUsersByValues(
808
-		string $app,
809
-		string $key,
810
-		array $values,
811
-		array $result,
812
-	): void {
813
-		$userConfig = $this->generateUserConfig();
814
-		$this->assertEqualsCanonicalizing($result, iterator_to_array($userConfig->searchUsersByValues($app, $key, $values)));
815
-	}
816
-
817
-	public static function providerSearchValuesByValueBool(): array {
818
-		return [
819
-			['app3', 'key10', true, ['user1', 'user4']],
820
-			['app3', 'key10', false, ['user2']],
821
-		];
822
-	}
823
-
824
-	/**
825
-	 * @dataProvider providerSearchValuesByValueBool
826
-	 */
827
-	public function testSearchUsersByValueBool(
828
-		string $app,
829
-		string $key,
830
-		bool $value,
831
-		array $result,
832
-	): void {
833
-		$userConfig = $this->generateUserConfig();
834
-		$this->assertEqualsCanonicalizing($result, iterator_to_array($userConfig->searchUsersByValueBool($app, $key, $value)));
835
-	}
836
-
837
-	public static function providerGetValueMixed(): array {
838
-		return [
839
-			[
840
-				['user1'], 'user1', 'app1', 'key0', 'default_because_unknown_key', true,
841
-				'default_because_unknown_key'
842
-			],
843
-			[
844
-				null, 'user1', 'app1', 'key0', 'default_because_unknown_key', true,
845
-				'default_because_unknown_key'
846
-			],
847
-			[
848
-				['user1'], 'user1', 'app1', 'key0', 'default_because_unknown_key', false,
849
-				'default_because_unknown_key'
850
-			],
851
-			[
852
-				null, 'user1', 'app1', 'key0', 'default_because_unknown_key', false,
853
-				'default_because_unknown_key'
854
-			],
855
-			[['user1'], 'user1', 'app1', 'fast_string', 'default_because_unknown_key', false, 'f_value'],
856
-			[null, 'user1', 'app1', 'fast_string', 'default_because_unknown_key', false, 'f_value'],
857
-			[['user1'], 'user1', 'app1', 'fast_string', 'default_because_unknown_key', true, 'f_value'],
858
-			// because non-lazy are already loaded
859
-			[
860
-				null, 'user1', 'app1', 'fast_string', 'default_because_unknown_key', true,
861
-				'default_because_unknown_key'
862
-			],
863
-			[
864
-				['user1'], 'user1', 'app1', 'lazy_string', 'default_because_unknown_key', false,
865
-				'default_because_unknown_key'
866
-			],
867
-			[
868
-				null, 'user1', 'app1', 'lazy_string', 'default_because_unknown_key', false,
869
-				'default_because_unknown_key'
870
-			],
871
-			[['user1'], 'user1', 'app1', 'lazy_string', 'default_because_unknown_key', true, 'l_value'],
872
-			[null, 'user1', 'app1', 'lazy_string', 'default_because_unknown_key', true, 'l_value'],
873
-			[
874
-				['user1'], 'user1', 'app1', 'fast_string_sensitive', 'default_because_unknown_key', false,
875
-				'fs_value'
876
-			],
877
-			[
878
-				null, 'user1', 'app1', 'fast_string_sensitive', 'default_because_unknown_key', false,
879
-				'fs_value'
880
-			],
881
-			[
882
-				['user1'], 'user1', 'app1', 'fast_string_sensitive', 'default_because_unknown_key', true,
883
-				'fs_value'
884
-			],
885
-			[
886
-				null, 'user1', 'app1', 'fast_string_sensitive', 'default_because_unknown_key', true,
887
-				'default_because_unknown_key'
888
-			],
889
-			[
890
-				['user1'], 'user1', 'app1', 'lazy_string_sensitive', 'default_because_unknown_key', false,
891
-				'default_because_unknown_key'
892
-			],
893
-			[
894
-				null, 'user1', 'app1', 'lazy_string_sensitive', 'default_because_unknown_key', false,
895
-				'default_because_unknown_key'
896
-			],
897
-			[
898
-				['user1'], 'user1', 'app1', 'lazy_string_sensitive', 'default_because_unknown_key', true,
899
-				'ls_value'
900
-			],
901
-			[null, 'user1', 'app1', 'lazy_string_sensitive', 'default_because_unknown_key', true, 'ls_value'],
902
-		];
903
-	}
904
-
905
-	/**
906
-	 * @dataProvider providerGetValueMixed
907
-	 */
908
-	public function testGetValueMixed(
909
-		?array $preload,
910
-		string $userId,
911
-		string $app,
912
-		string $key,
913
-		string $default,
914
-		bool $lazy,
915
-		string $result,
916
-	): void {
917
-		$userConfig = $this->generateUserConfig($preload ?? []);
918
-		$this->assertEquals($result, $userConfig->getValueMixed($userId, $app, $key, $default, $lazy));
919
-	}
920
-
921
-	/**
922
-	 * @dataProvider providerGetValueMixed
923
-	 */
924
-	public function testGetValueString(
925
-		?array $preload,
926
-		string $userId,
927
-		string $app,
928
-		string $key,
929
-		string $default,
930
-		bool $lazy,
931
-		string $result,
932
-	): void {
933
-		$userConfig = $this->generateUserConfig($preload ?? []);
934
-		$this->assertEquals($result, $userConfig->getValueString($userId, $app, $key, $default, $lazy));
935
-	}
936
-
937
-	public static function providerGetValueInt(): array {
938
-		return [
939
-			[['user1'], 'user1', 'app1', 'key0', 54321, true, 54321],
940
-			[null, 'user1', 'app1', 'key0', 54321, true, 54321],
941
-			[['user1'], 'user1', 'app1', 'key0', 54321, false, 54321],
942
-			[null, 'user1', 'app1', 'key0', 54321, false, 54321],
943
-			[null, 'user1', 'app1', 'key22', 54321, false, 31],
944
-			[['user1'], 'user1', 'app1', 'fast_int', 54321, false, 11],
945
-			[null, 'user1', 'app1', 'fast_int', 54321, false, 11],
946
-			[['user1'], 'user1', 'app1', 'fast_int', 54321, true, 11],
947
-			[null, 'user1', 'app1', 'fast_int', 54321, true, 54321],
948
-			[['user1'], 'user1', 'app1', 'fast_int_sensitive', 54321, false, 2024],
949
-			[null, 'user1', 'app1', 'fast_int_sensitive', 54321, false, 2024],
950
-			[['user1'], 'user1', 'app1', 'fast_int_sensitive', 54321, true, 2024],
951
-			[null, 'user1', 'app1', 'fast_int_sensitive', 54321, true, 54321],
952
-			[['user1'], 'user1', 'app1', 'lazy_int', 54321, false, 54321],
953
-			[null, 'user1', 'app1', 'lazy_int', 54321, false, 54321],
954
-			[['user1'], 'user1', 'app1', 'lazy_int', 54321, true, 12],
955
-			[null, 'user1', 'app1', 'lazy_int', 54321, true, 12],
956
-			[['user1'], 'user1', 'app1', 'lazy_int_sensitive', 54321, false, 54321],
957
-			[null, 'user1', 'app1', 'lazy_int_sensitive', 54321, false, 54321],
958
-			[['user1'], 'user1', 'app1', 'lazy_int_sensitive', 54321, true, 2048],
959
-			[null, 'user1', 'app1', 'lazy_int_sensitive', 54321, true, 2048],
960
-		];
961
-	}
962
-
963
-	/**
964
-	 * @dataProvider providerGetValueInt
965
-	 */
966
-	public function testGetValueInt(
967
-		?array $preload,
968
-		string $userId,
969
-		string $app,
970
-		string $key,
971
-		int $default,
972
-		bool $lazy,
973
-		int $result,
974
-	): void {
975
-		$userConfig = $this->generateUserConfig($preload ?? []);
976
-		$this->assertEquals($result, $userConfig->getValueInt($userId, $app, $key, $default, $lazy));
977
-	}
978
-
979
-	public static function providerGetValueFloat(): array {
980
-		return [
981
-			[['user1'], 'user1', 'app1', 'key0', 54.321, true, 54.321],
982
-			[null, 'user1', 'app1', 'key0', 54.321, true, 54.321],
983
-			[['user1'], 'user1', 'app1', 'key0', 54.321, false, 54.321],
984
-			[null, 'user1', 'app1', 'key0', 54.321, false, 54.321],
985
-			[['user1'], 'user1', 'app1', 'fast_float', 54.321, false, 3.14],
986
-			[null, 'user1', 'app1', 'fast_float', 54.321, false, 3.14],
987
-			[['user1'], 'user1', 'app1', 'fast_float', 54.321, true, 3.14],
988
-			[null, 'user1', 'app1', 'fast_float', 54.321, true, 54.321],
989
-			[['user1'], 'user1', 'app1', 'fast_float_sensitive', 54.321, false, 1.41],
990
-			[null, 'user1', 'app1', 'fast_float_sensitive', 54.321, false, 1.41],
991
-			[['user1'], 'user1', 'app1', 'fast_float_sensitive', 54.321, true, 1.41],
992
-			[null, 'user1', 'app1', 'fast_float_sensitive', 54.321, true, 54.321],
993
-			[['user1'], 'user1', 'app1', 'lazy_float', 54.321, false, 54.321],
994
-			[null, 'user1', 'app1', 'lazy_float', 54.321, false, 54.321],
995
-			[['user1'], 'user1', 'app1', 'lazy_float', 54.321, true, 3.14159],
996
-			[null, 'user1', 'app1', 'lazy_float', 54.321, true, 3.14159],
997
-			[['user1'], 'user1', 'app1', 'lazy_float_sensitive', 54.321, false, 54.321],
998
-			[null, 'user1', 'app1', 'lazy_float_sensitive', 54.321, false, 54.321],
999
-			[['user1'], 'user1', 'app1', 'lazy_float_sensitive', 54.321, true, 1.4142],
1000
-			[null, 'user1', 'app1', 'lazy_float_sensitive', 54.321, true, 1.4142],
1001
-		];
1002
-	}
1003
-
1004
-	/**
1005
-	 * @dataProvider providerGetValueFloat
1006
-	 */
1007
-	public function testGetValueFloat(
1008
-		?array $preload,
1009
-		string $userId,
1010
-		string $app,
1011
-		string $key,
1012
-		float $default,
1013
-		bool $lazy,
1014
-		float $result,
1015
-	): void {
1016
-		$userConfig = $this->generateUserConfig($preload ?? []);
1017
-		$this->assertEquals($result, $userConfig->getValueFloat($userId, $app, $key, $default, $lazy));
1018
-	}
1019
-
1020
-	public static function providerGetValueBool(): array {
1021
-		return [
1022
-			[['user1'], 'user1', 'app1', 'key0', false, true, false],
1023
-			[null, 'user1', 'app1', 'key0', false, true, false],
1024
-			[['user1'], 'user1', 'app1', 'key0', true, true, true],
1025
-			[null, 'user1', 'app1', 'key0', true, true, true],
1026
-			[['user1'], 'user1', 'app1', 'key0', false, false, false],
1027
-			[null, 'user1', 'app1', 'key0', false, false, false],
1028
-			[['user1'], 'user1', 'app1', 'key0', true, false, true],
1029
-			[null, 'user1', 'app1', 'key0', true, false, true],
1030
-			[['user1'], 'user1', 'app1', 'fast_boolean', false, false, true],
1031
-			[null, 'user1', 'app1', 'fast_boolean', false, false, true],
1032
-			[['user1'], 'user1', 'app1', 'fast_boolean_0', false, false, false],
1033
-			[null, 'user1', 'app1', 'fast_boolean_0', false, false, false],
1034
-			[['user1'], 'user1', 'app1', 'fast_boolean', true, false, true],
1035
-			[null, 'user1', 'app1', 'fast_boolean', true, false, true],
1036
-			[['user1'], 'user1', 'app1', 'fast_boolean_0', true, false, false],
1037
-			[null, 'user1', 'app1', 'fast_boolean_0', true, false, false],
1038
-			[['user1'], 'user1', 'app1', 'fast_boolean', false, true, true],
1039
-			[null, 'user1', 'app1', 'fast_boolean', false, true, false],
1040
-			[['user1'], 'user1', 'app1', 'fast_boolean_0', false, true, false],
1041
-			[null, 'user1', 'app1', 'fast_boolean_0', false, true, false],
1042
-			[['user1'], 'user1', 'app1', 'fast_boolean', true, true, true],
1043
-			[null, 'user1', 'app1', 'fast_boolean', true, true, true],
1044
-			[['user1'], 'user1', 'app1', 'fast_boolean_0', true, true, false],
1045
-			[null, 'user1', 'app1', 'fast_boolean_0', true, true, true],
1046
-			[['user1'], 'user1', 'app1', 'lazy_boolean', false, false, false],
1047
-			[null, 'user1', 'app1', 'lazy_boolean', false, false, false],
1048
-			[['user1'], 'user1', 'app1', 'lazy_boolean_0', false, false, false],
1049
-			[null, 'user1', 'app1', 'lazy_boolean_0', false, false, false],
1050
-			[['user1'], 'user1', 'app1', 'lazy_boolean', true, false, true],
1051
-			[null, 'user1', 'app1', 'lazy_boolean', true, false, true],
1052
-			[['user1'], 'user1', 'app1', 'lazy_boolean_0', true, false, true],
1053
-			[null, 'user1', 'app1', 'lazy_boolean_0', true, false, true],
1054
-			[['user1'], 'user1', 'app1', 'lazy_boolean', false, true, true],
1055
-			[null, 'user1', 'app1', 'lazy_boolean', false, true, true],
1056
-			[['user1'], 'user1', 'app1', 'lazy_boolean_0', false, true, false],
1057
-			[null, 'user1', 'app1', 'lazy_boolean_0', false, true, false],
1058
-			[['user1'], 'user1', 'app1', 'lazy_boolean', true, true, true],
1059
-			[null, 'user1', 'app1', 'lazy_boolean', true, true, true],
1060
-			[['user1'], 'user1', 'app1', 'lazy_boolean_0', true, true, false],
1061
-			[null, 'user1', 'app1', 'lazy_boolean_0', true, true, false],
1062
-		];
1063
-	}
1064
-
1065
-	/**
1066
-	 * @dataProvider providerGetValueBool
1067
-	 */
1068
-	public function testGetValueBool(
1069
-		?array $preload,
1070
-		string $userId,
1071
-		string $app,
1072
-		string $key,
1073
-		bool $default,
1074
-		bool $lazy,
1075
-		bool $result,
1076
-	): void {
1077
-		$userConfig = $this->generateUserConfig($preload ?? []);
1078
-		$this->assertEquals($result, $userConfig->getValueBool($userId, $app, $key, $default, $lazy));
1079
-	}
1080
-
1081
-	public static function providerGetValueArray(): array {
1082
-		return [
1083
-			[
1084
-				['user1'], 'user1', 'app1', 'key0', ['default_because_unknown_key'], true,
1085
-				['default_because_unknown_key']
1086
-			],
1087
-			[
1088
-				null, 'user1', 'app1', 'key0', ['default_because_unknown_key'], true,
1089
-				['default_because_unknown_key']
1090
-			],
1091
-			[
1092
-				['user1'], 'user1', 'app1', 'key0', ['default_because_unknown_key'], false,
1093
-				['default_because_unknown_key']
1094
-			],
1095
-			[
1096
-				null, 'user1', 'app1', 'key0', ['default_because_unknown_key'], false,
1097
-				['default_because_unknown_key']
1098
-			],
1099
-		];
1100
-	}
1101
-
1102
-	/**
1103
-	 * @dataProvider providerGetValueArray
1104
-	 */
1105
-	public function testGetValueArray(
1106
-		?array $preload,
1107
-		string $userId,
1108
-		string $app,
1109
-		string $key,
1110
-		array $default,
1111
-		bool $lazy,
1112
-		array $result,
1113
-	): void {
1114
-		$userConfig = $this->generateUserConfig($preload ?? []);
1115
-		$this->assertEqualsCanonicalizing(
1116
-			$result, $userConfig->getValueArray($userId, $app, $key, $default, $lazy)
1117
-		);
1118
-	}
1119
-
1120
-	public static function providerGetValueType(): array {
1121
-		return [
1122
-			[null, 'user1', 'app1', 'key1', false, ValueType::MIXED],
1123
-			[null, 'user1', 'app1', 'key1', true, null, UnknownKeyException::class],
1124
-			[null, 'user1', 'app1', 'fast_string', true, ValueType::STRING, UnknownKeyException::class],
1125
-			[['user1'], 'user1', 'app1', 'fast_string', true, ValueType::STRING],
1126
-			[null, 'user1', 'app1', 'fast_string', false, ValueType::STRING],
1127
-			[null, 'user1', 'app1', 'lazy_string', true, ValueType::STRING],
1128
-			[null, 'user1', 'app1', 'lazy_string', false, ValueType::STRING, UnknownKeyException::class],
1129
-			[
1130
-				null, 'user1', 'app1', 'fast_string_sensitive', true, ValueType::STRING,
1131
-				UnknownKeyException::class
1132
-			],
1133
-			[['user1'], 'user1', 'app1', 'fast_string_sensitive', true, ValueType::STRING],
1134
-			[null, 'user1', 'app1', 'fast_string_sensitive', false, ValueType::STRING],
1135
-			[null, 'user1', 'app1', 'lazy_string_sensitive', true, ValueType::STRING],
1136
-			[
1137
-				null, 'user1', 'app1', 'lazy_string_sensitive', false, ValueType::STRING,
1138
-				UnknownKeyException::class
1139
-			],
1140
-			[null, 'user1', 'app1', 'fast_int', true, ValueType::INT, UnknownKeyException::class],
1141
-			[['user1'], 'user1', 'app1', 'fast_int', true, ValueType::INT],
1142
-			[null, 'user1', 'app1', 'fast_int', false, ValueType::INT],
1143
-			[null, 'user1', 'app1', 'lazy_int', true, ValueType::INT],
1144
-			[null, 'user1', 'app1', 'lazy_int', false, ValueType::INT, UnknownKeyException::class],
1145
-			[null, 'user1', 'app1', 'fast_float', true, ValueType::FLOAT, UnknownKeyException::class],
1146
-			[['user1'], 'user1', 'app1', 'fast_float', true, ValueType::FLOAT],
1147
-			[null, 'user1', 'app1', 'fast_float', false, ValueType::FLOAT],
1148
-			[null, 'user1', 'app1', 'lazy_float', true, ValueType::FLOAT],
1149
-			[null, 'user1', 'app1', 'lazy_float', false, ValueType::FLOAT, UnknownKeyException::class],
1150
-			[null, 'user1', 'app1', 'fast_boolean', true, ValueType::BOOL, UnknownKeyException::class],
1151
-			[['user1'], 'user1', 'app1', 'fast_boolean', true, ValueType::BOOL],
1152
-			[null, 'user1', 'app1', 'fast_boolean', false, ValueType::BOOL],
1153
-			[null, 'user1', 'app1', 'lazy_boolean', true, ValueType::BOOL],
1154
-			[null, 'user1', 'app1', 'lazy_boolean', false, ValueType::BOOL, UnknownKeyException::class],
1155
-		];
1156
-	}
1157
-
1158
-	/**
1159
-	 * @dataProvider providerGetValueType
1160
-	 */
1161
-	public function testGetValueType(
1162
-		?array $preload,
1163
-		string $userId,
1164
-		string $app,
1165
-		string $key,
1166
-		?bool $lazy,
1167
-		?ValueType $result,
1168
-		?string $exception = null,
1169
-	): void {
1170
-		$userConfig = $this->generateUserConfig($preload ?? []);
1171
-		if ($exception !== null) {
1172
-			$this->expectException($exception);
1173
-		}
1174
-
1175
-		$type = $userConfig->getValueType($userId, $app, $key, $lazy);
1176
-		if ($exception === null) {
1177
-			$this->assertEquals($result->value, $type->value);
1178
-		}
1179
-	}
1180
-
1181
-	public static function providerSetValueMixed(): array {
1182
-		return [
1183
-			[null, 'user1', 'app1', 'key1', 'value', false, false, true],
1184
-			[null, 'user1', 'app1', 'key1', '12345', true, false, true],
1185
-			[null, 'user1', 'app1', 'key1', '12345', true, true, true],
1186
-			[null, 'user1', 'app1', 'key1', 'value1', false, false, false],
1187
-			[null, 'user1', 'app1', 'key1', 'value1', true, false, true],
1188
-			[null, 'user1', 'app1', 'key1', 'value1', false, true, true],
1189
-			[
1190
-				null, 'user1', 'app1', 'fast_string', 'f_value_2', false, false, true,
1191
-				TypeConflictException::class
1192
-			],
1193
-			[
1194
-				null, 'user1', 'app1', 'fast_string', 'f_value', true, false, true,
1195
-				TypeConflictException::class
1196
-			],
1197
-			[null, 'user1', 'app1', 'fast_string', 'f_value', true, true, true, TypeConflictException::class],
1198
-			[null, 'user1', 'app1', 'fast_int', 'n_value', false, false, true, TypeConflictException::class],
1199
-			[
1200
-				null, 'user1', 'app1', 'fast_float', 'n_value', false, false, true,
1201
-				TypeConflictException::class
1202
-			],
1203
-			[
1204
-				null, 'user1', 'app1', 'lazy_string', 'l_value_2', false, false, true,
1205
-				TypeConflictException::class
1206
-			],
1207
-			[null, 'user1', 'app1', 'lazy_string', 'l_value', true, false, false],
1208
-			[null, 'user1', 'app1', 'lazy_string', 'l_value', true, true, true, TypeConflictException::class],
1209
-			[null, 'user1', 'app1', 'lazy_int', 'l_value', false, false, true, TypeConflictException::class],
1210
-			[
1211
-				null, 'user1', 'app1', 'lazy_float', 'l_value', false, false, true,
1212
-				TypeConflictException::class
1213
-			],
1214
-		];
1215
-	}
1216
-
1217
-	/**
1218
-	 * @dataProvider providerSetValueMixed
1219
-	 */
1220
-	public function testSetValueMixed(
1221
-		?array $preload,
1222
-		string $userId,
1223
-		string $app,
1224
-		string $key,
1225
-		string $value,
1226
-		bool $lazy,
1227
-		bool $sensitive,
1228
-		bool $result,
1229
-		?string $exception = null,
1230
-	): void {
1231
-		$userConfig = $this->generateUserConfig($preload ?? []);
1232
-		if ($exception !== null) {
1233
-			$this->expectException($exception);
1234
-		}
1235
-
1236
-		$edited = $userConfig->setValueMixed($userId, $app, $key, $value, $lazy, ($sensitive) ? 1 : 0);
1237
-
1238
-		if ($exception === null) {
1239
-			$this->assertEquals($result, $edited);
1240
-		}
1241
-	}
1242
-
1243
-
1244
-	public static function providerSetValueString(): array {
1245
-		return [
1246
-			[null, 'user1', 'app1', 'key1', 'value', false, false, true],
1247
-			[null, 'user1', 'app1', 'key1', '12345', true, false, true],
1248
-			[null, 'user1', 'app1', 'key1', '12345', true, true, true],
1249
-			[null, 'user1', 'app1', 'key1', 'value1', false, false, false],
1250
-			[null, 'user1', 'app1', 'key1', 'value1', true, false, true],
1251
-			[null, 'user1', 'app1', 'key1', 'value1', false, true, true],
1252
-			[null, 'user1', 'app1', 'fast_string', 'f_value_2', false, false, true],
1253
-			[null, 'user1', 'app1', 'fast_string', 'f_value', false, false, false],
1254
-			[null, 'user1', 'app1', 'fast_string', 'f_value', true, false, true],
1255
-			[null, 'user1', 'app1', 'fast_string', 'f_value', true, true, true],
1256
-			[null, 'user1', 'app1', 'lazy_string', 'l_value_2', false, false, true],
1257
-			[null, 'user1', 'app1', 'lazy_string', 'l_value', true, false, false],
1258
-			[null, 'user1', 'app1', 'lazy_string', 'l_value', true, true, true],
1259
-			[null, 'user1', 'app1', 'fast_string_sensitive', 'fs_value', false, true, false],
1260
-			[null, 'user1', 'app1', 'fast_string_sensitive', 'fs_value', true, true, true],
1261
-			[null, 'user1', 'app1', 'fast_string_sensitive', 'fs_value', true, false, true],
1262
-			[null, 'user1', 'app1', 'lazy_string_sensitive', 'ls_value', false, true, true],
1263
-			[null, 'user1', 'app1', 'lazy_string_sensitive', 'ls_value', true, true, false],
1264
-			[null, 'user1', 'app1', 'lazy_string_sensitive', 'ls_value', true, false, false],
1265
-			[null, 'user1', 'app1', 'lazy_string_sensitive', 'ls_value_2', true, false, true],
1266
-			[null, 'user1', 'app1', 'fast_int', 'n_value', false, false, true, TypeConflictException::class],
1267
-			[
1268
-				null, 'user1', 'app1', 'fast_float', 'n_value', false, false, true,
1269
-				TypeConflictException::class
1270
-			],
1271
-			[
1272
-				null, 'user1', 'app1', 'fast_float', 'n_value', false, false, true,
1273
-				TypeConflictException::class
1274
-			],
1275
-			[null, 'user1', 'app1', 'lazy_int', 'n_value', false, false, true, TypeConflictException::class],
1276
-			[
1277
-				null, 'user1', 'app1', 'lazy_boolean', 'n_value', false, false, true,
1278
-				TypeConflictException::class
1279
-			],
1280
-			[
1281
-				null, 'user1', 'app1', 'lazy_float', 'n_value', false, false, true,
1282
-				TypeConflictException::class
1283
-			],
1284
-		];
1285
-	}
1286
-
1287
-	/**
1288
-	 * @dataProvider providerSetValueString
1289
-	 */
1290
-	public function testSetValueString(
1291
-		?array $preload,
1292
-		string $userId,
1293
-		string $app,
1294
-		string $key,
1295
-		string $value,
1296
-		bool $lazy,
1297
-		bool $sensitive,
1298
-		bool $result,
1299
-		?string $exception = null,
1300
-	): void {
1301
-		$userConfig = $this->generateUserConfig($preload ?? []);
1302
-		if ($exception !== null) {
1303
-			$this->expectException($exception);
1304
-		}
1305
-
1306
-		$edited = $userConfig->setValueString($userId, $app, $key, $value, $lazy, ($sensitive) ? 1 : 0);
1307
-		if ($exception !== null) {
1308
-			return;
1309
-		}
1310
-
1311
-		$this->assertEquals($result, $edited);
1312
-		if ($result) {
1313
-			$this->assertEquals($value, $userConfig->getValueString($userId, $app, $key, $value, $lazy));
1314
-			$userConfig = $this->generateUserConfig($preload ?? []);
1315
-			$this->assertEquals($value, $userConfig->getValueString($userId, $app, $key, $value, $lazy));
1316
-		}
1317
-	}
1318
-
1319
-	public static function providerSetValueInt(): array {
1320
-		return [
1321
-			[null, 'user1', 'app1', 'key1', 12345, false, false, true],
1322
-			[null, 'user1', 'app1', 'key1', 12345, true, false, true],
1323
-			[null, 'user1', 'app1', 'key1', 12345, true, true, true],
1324
-			[null, 'user1', 'app1', 'fast_int', 11, false, false, false],
1325
-			[null, 'user1', 'app1', 'fast_int', 111, false, false, true],
1326
-			[null, 'user1', 'app1', 'fast_int', 111, true, false, true],
1327
-			[null, 'user1', 'app1', 'fast_int', 111, false, true, true],
1328
-			[null, 'user1', 'app1', 'fast_int', 11, true, false, true],
1329
-			[null, 'user1', 'app1', 'fast_int', 11, false, true, true],
1330
-			[null, 'user1', 'app1', 'lazy_int', 12, false, false, true],
1331
-			[null, 'user1', 'app1', 'lazy_int', 121, false, false, true],
1332
-			[null, 'user1', 'app1', 'lazy_int', 121, true, false, true],
1333
-			[null, 'user1', 'app1', 'lazy_int', 121, false, true, true],
1334
-			[null, 'user1', 'app1', 'lazy_int', 12, true, false, false],
1335
-			[null, 'user1', 'app1', 'lazy_int', 12, false, true, true],
1336
-			[null, 'user1', 'app1', 'fast_string', 12345, false, false, true, TypeConflictException::class],
1337
-			[null, 'user1', 'app1', 'fast_string', 12345, false, false, false, TypeConflictException::class],
1338
-			[null, 'user1', 'app1', 'fast_string', 12345, true, false, true, TypeConflictException::class],
1339
-			[null, 'user1', 'app1', 'fast_string', 12345, true, true, true, TypeConflictException::class],
1340
-			[null, 'user1', 'app1', 'lazy_string', 12345, false, false, true, TypeConflictException::class],
1341
-			[null, 'user1', 'app1', 'lazy_string', 12345, true, false, false, TypeConflictException::class],
1342
-			[null, 'user1', 'app1', 'lazy_string', 12345, true, true, true, TypeConflictException::class],
1343
-			[null, 'user1', 'app1', 'fast_float', 12345, false, false, true, TypeConflictException::class],
1344
-			[null, 'user1', 'app1', 'fast_float', 12345, false, false, true, TypeConflictException::class],
1345
-			[null, 'user1', 'app1', 'lazy_boolean', 12345, false, false, true, TypeConflictException::class],
1346
-			[null, 'user1', 'app1', 'lazy_float', 12345, false, false, true, TypeConflictException::class],
1347
-		];
1348
-	}
1349
-
1350
-	/**
1351
-	 * @dataProvider providerSetValueInt
1352
-	 */
1353
-	public function testSetValueInt(
1354
-		?array $preload,
1355
-		string $userId,
1356
-		string $app,
1357
-		string $key,
1358
-		int $value,
1359
-		bool $lazy,
1360
-		bool $sensitive,
1361
-		bool $result,
1362
-		?string $exception = null,
1363
-	): void {
1364
-		$userConfig = $this->generateUserConfig($preload ?? []);
1365
-		if ($exception !== null) {
1366
-			$this->expectException($exception);
1367
-		}
1368
-
1369
-		$edited = $userConfig->setValueInt($userId, $app, $key, $value, $lazy, ($sensitive) ? 1 : 0);
1370
-
1371
-		if ($exception !== null) {
1372
-			return;
1373
-		}
1374
-
1375
-		$this->assertEquals($result, $edited);
1376
-		if ($result) {
1377
-			$this->assertEquals($value, $userConfig->getValueInt($userId, $app, $key, $value, $lazy));
1378
-			$userConfig = $this->generateUserConfig($preload ?? []);
1379
-			$this->assertEquals($value, $userConfig->getValueInt($userId, $app, $key, $value, $lazy));
1380
-		}
1381
-	}
1382
-
1383
-	public static function providerSetValueFloat(): array {
1384
-		return [
1385
-			[null, 'user1', 'app1', 'key1', 12.345, false, false, true],
1386
-			[null, 'user1', 'app1', 'key1', 12.345, true, false, true],
1387
-			[null, 'user1', 'app1', 'key1', 12.345, true, true, true],
1388
-			[null, 'user1', 'app1', 'fast_float', 3.14, false, false, false],
1389
-			[null, 'user1', 'app1', 'fast_float', 3.15, false, false, true],
1390
-			[null, 'user1', 'app1', 'fast_float', 3.15, true, false, true],
1391
-			[null, 'user1', 'app1', 'fast_float', 3.15, false, true, true],
1392
-			[null, 'user1', 'app1', 'fast_float', 3.14, true, false, true],
1393
-			[null, 'user1', 'app1', 'fast_float', 3.14, false, true, true],
1394
-			[null, 'user1', 'app1', 'lazy_float', 3.14159, false, false, true],
1395
-			[null, 'user1', 'app1', 'lazy_float', 3.14158, false, false, true],
1396
-			[null, 'user1', 'app1', 'lazy_float', 3.14158, true, false, true],
1397
-			[null, 'user1', 'app1', 'lazy_float', 3.14158, false, true, true],
1398
-			[null, 'user1', 'app1', 'lazy_float', 3.14159, true, false, false],
1399
-			[null, 'user1', 'app1', 'lazy_float', 3.14159, false, true, true],
1400
-			[null, 'user1', 'app1', 'fast_string', 12.345, false, false, true, TypeConflictException::class],
1401
-			[null, 'user1', 'app1', 'fast_string', 12.345, false, false, false, TypeConflictException::class],
1402
-			[null, 'user1', 'app1', 'fast_string', 12.345, true, false, true, TypeConflictException::class],
1403
-			[null, 'user1', 'app1', 'fast_string', 12.345, true, true, true, TypeConflictException::class],
1404
-			[null, 'user1', 'app1', 'lazy_string', 12.345, false, false, true, TypeConflictException::class],
1405
-			[null, 'user1', 'app1', 'lazy_string', 12.345, true, false, false, TypeConflictException::class],
1406
-			[null, 'user1', 'app1', 'fast_array', 12.345, true, true, true, TypeConflictException::class],
1407
-			[null, 'user1', 'app1', 'fast_int', 12.345, false, false, true, TypeConflictException::class],
1408
-			[null, 'user1', 'app1', 'fast_int', 12.345, false, false, true, TypeConflictException::class],
1409
-			[null, 'user1', 'app1', 'lazy_boolean', 12.345, false, false, true, TypeConflictException::class],
1410
-		];
1411
-	}
1412
-
1413
-	/**
1414
-	 * @dataProvider providerSetValueFloat
1415
-	 */
1416
-	public function testSetValueFloat(
1417
-		?array $preload,
1418
-		string $userId,
1419
-		string $app,
1420
-		string $key,
1421
-		float $value,
1422
-		bool $lazy,
1423
-		bool $sensitive,
1424
-		bool $result,
1425
-		?string $exception = null,
1426
-	): void {
1427
-		$userConfig = $this->generateUserConfig($preload ?? []);
1428
-		if ($exception !== null) {
1429
-			$this->expectException($exception);
1430
-		}
1431
-
1432
-		$edited = $userConfig->setValueFloat($userId, $app, $key, $value, $lazy, ($sensitive) ? 1 : 0);
1433
-
1434
-		if ($exception !== null) {
1435
-			return;
1436
-		}
1437
-
1438
-		$this->assertEquals($result, $edited);
1439
-		if ($result) {
1440
-			$this->assertEquals($value, $userConfig->getValueFloat($userId, $app, $key, $value, $lazy));
1441
-			$userConfig = $this->generateUserConfig($preload ?? []);
1442
-			$this->assertEquals($value, $userConfig->getValueFloat($userId, $app, $key, $value, $lazy));
1443
-		}
1444
-	}
1445
-
1446
-
1447
-	public static function providerSetValueArray(): array {
1448
-		return [
1449
-			[null, 'user1', 'app1', 'key1', [], false, false, true],
1450
-			[null, 'user1', 'app1', 'key1', [], true, false, true],
1451
-			[null, 'user1', 'app1', 'key1', [], true, true, true],
1452
-			[null, 'user1', 'app1', 'fast_array', ['year' => 2024], false, false, false],
1453
-			[null, 'user1', 'app1', 'fast_array', [], false, false, true],
1454
-			[null, 'user1', 'app1', 'fast_array', [], true, false, true],
1455
-			[null, 'user1', 'app1', 'fast_array', [], false, true, true],
1456
-			[null, 'user1', 'app1', 'fast_array', ['year' => 2024], true, false, true],
1457
-			[null, 'user1', 'app1', 'fast_array', ['year' => 2024], false, true, true],
1458
-			[null, 'user1', 'app1', 'lazy_array', ['month' => 'October'], false, false, true],
1459
-			[null, 'user1', 'app1', 'lazy_array', ['month' => 'September'], false, false, true],
1460
-			[null, 'user1', 'app1', 'lazy_array', ['month' => 'September'], true, false, true],
1461
-			[null, 'user1', 'app1', 'lazy_array', ['month' => 'September'], false, true, true],
1462
-			[null, 'user1', 'app1', 'lazy_array', ['month' => 'October'], true, false, false],
1463
-			[null, 'user1', 'app1', 'lazy_array', ['month' => 'October'], false, true, true],
1464
-			[null, 'user1', 'app1', 'fast_string', [], false, false, true, TypeConflictException::class],
1465
-			[null, 'user1', 'app1', 'fast_string', [], false, false, false, TypeConflictException::class],
1466
-			[null, 'user1', 'app1', 'fast_string', [], true, false, true, TypeConflictException::class],
1467
-			[null, 'user1', 'app1', 'fast_string', [], true, true, true, TypeConflictException::class],
1468
-			[null, 'user1', 'app1', 'lazy_string', [], false, false, true, TypeConflictException::class],
1469
-			[null, 'user1', 'app1', 'lazy_string', [], true, false, false, TypeConflictException::class],
1470
-			[null, 'user1', 'app1', 'lazy_string', [], true, true, true, TypeConflictException::class],
1471
-			[null, 'user1', 'app1', 'fast_int', [], false, false, true, TypeConflictException::class],
1472
-			[null, 'user1', 'app1', 'fast_int', [], false, false, true, TypeConflictException::class],
1473
-			[null, 'user1', 'app1', 'lazy_boolean', [], false, false, true, TypeConflictException::class],
1474
-		];
1475
-	}
1476
-
1477
-	/**
1478
-	 * @dataProvider providerSetValueArray
1479
-	 */
1480
-	public function testSetValueArray(
1481
-		?array $preload,
1482
-		string $userId,
1483
-		string $app,
1484
-		string $key,
1485
-		array $value,
1486
-		bool $lazy,
1487
-		bool $sensitive,
1488
-		bool $result,
1489
-		?string $exception = null,
1490
-	): void {
1491
-		$userConfig = $this->generateUserConfig($preload ?? []);
1492
-		if ($exception !== null) {
1493
-			$this->expectException($exception);
1494
-		}
1495
-
1496
-		$edited = $userConfig->setValueArray($userId, $app, $key, $value, $lazy, ($sensitive) ? 1 : 0);
1497
-
1498
-		if ($exception !== null) {
1499
-			return;
1500
-		}
1501
-
1502
-		$this->assertEquals($result, $edited);
1503
-		if ($result) {
1504
-			$this->assertEqualsCanonicalizing(
1505
-				$value, $userConfig->getValueArray($userId, $app, $key, $value, $lazy)
1506
-			);
1507
-			$userConfig = $this->generateUserConfig($preload ?? []);
1508
-			$this->assertEqualsCanonicalizing(
1509
-				$value, $userConfig->getValueArray($userId, $app, $key, $value, $lazy)
1510
-			);
1511
-		}
1512
-	}
1513
-
1514
-	public static function providerUpdateSensitive(): array {
1515
-		return [
1516
-			[null, 'user1', 'app1', 'key1', false, false],
1517
-			[['user1'], 'user1', 'app1', 'key1', false, false],
1518
-			[null, 'user1', 'app1', 'key1', true, true],
1519
-			[['user1'], 'user1', 'app1', 'key1', true, true],
1520
-		];
1521
-	}
1522
-
1523
-	/**
1524
-	 * @dataProvider providerUpdateSensitive
1525
-	 */
1526
-	public function testUpdateSensitive(
1527
-		?array $preload,
1528
-		string $userId,
1529
-		string $app,
1530
-		string $key,
1531
-		bool $sensitive,
1532
-		bool $result,
1533
-		?string $exception = null,
1534
-	): void {
1535
-		$userConfig = $this->generateUserConfig($preload ?? []);
1536
-		if ($exception !== null) {
1537
-			$this->expectException($exception);
1538
-		}
1539
-
1540
-		$edited = $userConfig->updateSensitive($userId, $app, $key, $sensitive);
1541
-		if ($exception !== null) {
1542
-			return;
1543
-		}
1544
-
1545
-		$this->assertEquals($result, $edited);
1546
-		if ($result) {
1547
-			$this->assertEquals($sensitive, $userConfig->isSensitive($userId, $app, $key));
1548
-			$userConfig = $this->generateUserConfig($preload ?? []);
1549
-			$this->assertEquals($sensitive, $userConfig->isSensitive($userId, $app, $key));
1550
-			if ($sensitive) {
1551
-				$this->assertEquals(true, str_starts_with(
1552
-					$userConfig->statusCache()['fastCache'][$userId][$app][$key] ??
1553
-					$userConfig->statusCache()['lazyCache'][$userId][$app][$key],
1554
-					'$UserConfigEncryption$')
1555
-				);
1556
-			}
1557
-		}
1558
-	}
1559
-
1560
-	public static function providerUpdateGlobalSensitive(): array {
1561
-		return [[true], [false]];
1562
-	}
1563
-
1564
-	/**
1565
-	 * @dataProvider providerUpdateGlobalSensitive
1566
-	 */
1567
-	public function testUpdateGlobalSensitive(bool $sensitive): void {
1568
-		$userConfig = $this->generateUserConfig($preload ?? []);
1569
-		$app = 'app2';
1570
-		if ($sensitive) {
1571
-			$key = 'key2';
1572
-			$value = 'value2a';
1573
-		} else {
1574
-			$key = 'key4';
1575
-			$value = 'value4';
1576
-		}
1577
-
1578
-		$this->assertEquals($value, $userConfig->getValueString('user1', $app, $key));
1579
-		foreach (['user1', 'user2', 'user3', 'user4'] as $userId) {
1580
-			$userConfig->getValueString($userId, $app, $key); // cache loading for userId
1581
-			$this->assertEquals(
1582
-				!$sensitive, str_starts_with(
1583
-					$userConfig->statusCache()['fastCache'][$userId][$app][$key] ??
1584
-					$userConfig->statusCache()['lazyCache'][$userId][$app][$key],
1585
-					'$UserConfigEncryption$'
1586
-				)
1587
-			);
1588
-		}
1589
-
1590
-		$userConfig->updateGlobalSensitive($app, $key, $sensitive);
1591
-
1592
-		$this->assertEquals($value, $userConfig->getValueString('user1', $app, $key));
1593
-		foreach (['user1', 'user2', 'user3', 'user4'] as $userId) {
1594
-			$this->assertEquals($sensitive, $userConfig->isSensitive($userId, $app, $key));
1595
-			// should only work if updateGlobalSensitive drop cache
1596
-			$this->assertEquals($sensitive, str_starts_with(
1597
-				$userConfig->statusCache()['fastCache'][$userId][$app][$key] ??
1598
-				$userConfig->statusCache()['lazyCache'][$userId][$app][$key],
1599
-				'$UserConfigEncryption$')
1600
-			);
1601
-		}
1602
-	}
1603
-
1604
-	public static function providerUpdateLazy(): array {
1605
-		return [
1606
-			[null, 'user1', 'app1', 'key1', false, false],
1607
-			[['user1'], 'user1', 'app1', 'key1', false, false],
1608
-			[null, 'user1', 'app1', 'key1', true, true],
1609
-			[['user1'], 'user1', 'app1', 'key1', true, true],
1610
-		];
1611
-	}
1612
-
1613
-	/**
1614
-	 * @dataProvider providerUpdateLazy
1615
-	 */
1616
-	public function testUpdateLazy(
1617
-		?array $preload,
1618
-		string $userId,
1619
-		string $app,
1620
-		string $key,
1621
-		bool $lazy,
1622
-		bool $result,
1623
-		?string $exception = null,
1624
-	): void {
1625
-		$userConfig = $this->generateUserConfig($preload ?? []);
1626
-		if ($exception !== null) {
1627
-			$this->expectException($exception);
1628
-		}
1629
-
1630
-		$edited = $userConfig->updateLazy($userId, $app, $key, $lazy);
1631
-		if ($exception !== null) {
1632
-			return;
1633
-		}
1634
-
1635
-		$this->assertEquals($result, $edited);
1636
-		if ($result) {
1637
-			$this->assertEquals($lazy, $userConfig->isLazy($userId, $app, $key));
1638
-			$userConfig = $this->generateUserConfig($preload ?? []);
1639
-			$this->assertEquals($lazy, $userConfig->isLazy($userId, $app, $key));
1640
-		}
1641
-	}
1642
-
1643
-	public static function providerUpdateGlobalLazy(): array {
1644
-		return [[true], [false]];
1645
-	}
1646
-
1647
-	/**
1648
-	 * @dataProvider providerUpdateGlobalLazy
1649
-	 */
1650
-	public function testUpdateGlobalLazy(bool $lazy): void {
1651
-		$userConfig = $this->generateUserConfig($preload ?? []);
1652
-		$app = 'app2';
1653
-		if ($lazy) {
1654
-			$key = 'key4';
1655
-			$value = 'value4';
1656
-		} else {
1657
-			$key = 'key3';
1658
-			$value = 'value3';
1659
-		}
1660
-
1661
-		$this->assertEquals($value, $userConfig->getValueString('user1', $app, $key, '', !$lazy));
1662
-		foreach (['user1', 'user2', 'user3', 'user4'] as $userId) {
1663
-			$this->assertEquals(!$lazy, $userConfig->isLazy($userId, $app, $key));
1664
-		}
1665
-
1666
-		$userConfig->updateGlobalLazy($app, $key, $lazy);
1667
-		$this->assertEquals($value, $userConfig->getValueString('user1', $app, $key, '', $lazy));
1668
-		foreach (['user1', 'user2', 'user3', 'user4'] as $userId) {
1669
-			$this->assertEquals($lazy, $userConfig->isLazy($userId, $app, $key));
1670
-		}
1671
-	}
1672
-
1673
-	public static function providerGetDetails(): array {
1674
-		return [
1675
-			[
1676
-				'user3', 'app2', 'key2',
1677
-				[
1678
-					'userId' => 'user3',
1679
-					'app' => 'app2',
1680
-					'key' => 'key2',
1681
-					'value' => 'value2c',
1682
-					'type' => 0,
1683
-					'lazy' => false,
1684
-					'typeString' => 'mixed',
1685
-					'sensitive' => false
1686
-				]
1687
-			],
1688
-			[
1689
-				'user1', 'app1', 'lazy_int',
1690
-				[
1691
-					'userId' => 'user1',
1692
-					'app' => 'app1',
1693
-					'key' => 'lazy_int',
1694
-					'value' => 12,
1695
-					'type' => 2,
1696
-					'lazy' => true,
1697
-					'typeString' => 'int',
1698
-					'sensitive' => false
1699
-				]
1700
-			],
1701
-			[
1702
-				'user1', 'app1', 'fast_float_sensitive',
1703
-				[
1704
-					'userId' => 'user1',
1705
-					'app' => 'app1',
1706
-					'key' => 'fast_float_sensitive',
1707
-					'value' => 1.41,
1708
-					'type' => 3,
1709
-					'lazy' => false,
1710
-					'typeString' => 'float',
1711
-					'sensitive' => true
1712
-				]
1713
-			],
1714
-		];
1715
-	}
1716
-
1717
-	/**
1718
-	 * @dataProvider providerGetDetails
1719
-	 */
1720
-	public function testGetDetails(string $userId, string $app, string $key, array $result): void {
1721
-		$userConfig = $this->generateUserConfig($preload ?? []);
1722
-		$this->assertEqualsCanonicalizing($result, $userConfig->getDetails($userId, $app, $key));
1723
-	}
1724
-
1725
-
1726
-	public static function providerDeletePreference(): array {
1727
-		return [
1728
-			[null, 'user1', 'app1', 'key22'],
1729
-			[['user1'], 'user1', 'app1', 'fast_string_sensitive'],
1730
-			[null, 'user1', 'app1', 'lazy_array_sensitive'],
1731
-			[['user2'], 'user1', 'app1', 'lazy_array_sensitive'],
1732
-			[null, 'user2', 'only-lazy', 'key1'],
1733
-			[['user2'], 'user2', 'only-lazy', 'key1'],
1734
-		];
1735
-	}
1736
-
1737
-	/**
1738
-	 * @dataProvider providerDeletePreference
1739
-	 */
1740
-	public function testDeletePreference(
1741
-		?array $preload,
1742
-		string $userId,
1743
-		string $app,
1744
-		string $key,
1745
-	): void {
1746
-		$userConfig = $this->generateUserConfig($preload ?? []);
1747
-		$lazy = $userConfig->isLazy($userId, $app, $key);
1748
-
1749
-		$userConfig = $this->generateUserConfig($preload ?? []);
1750
-		$this->assertEquals(true, $userConfig->hasKey($userId, $app, $key, $lazy));
1751
-		$userConfig->deleteUserConfig($userId, $app, $key);
1752
-		$this->assertEquals(false, $userConfig->hasKey($userId, $app, $key, $lazy));
1753
-		$userConfig = $this->generateUserConfig($preload ?? []);
1754
-		$this->assertEquals(false, $userConfig->hasKey($userId, $app, $key, $lazy));
1755
-	}
1756
-
1757
-	public static function providerDeleteKey(): array {
1758
-		return [
1759
-			[null, 'app2', 'key3'],
1760
-			[['user1'], 'app2', 'key3'],
1761
-			[null, 'only-lazy', 'key1'],
1762
-			[['user2'], 'only-lazy', 'key1'],
1763
-			[null, 'app2', 'lazy_string_sensitive'],
1764
-			[['user3', 'user1'], 'app2', 'lazy_string_sensitive'],
1765
-		];
1766
-	}
1767
-
1768
-	/**
1769
-	 * @dataProvider providerDeleteKey
1770
-	 */
1771
-	public function testDeleteKey(
1772
-		?array $preload,
1773
-		string $app,
1774
-		string $key,
1775
-	): void {
1776
-		$userConfig = $this->generateUserConfig($preload ?? []);
1777
-		$userConfig->deleteKey($app, $key);
1778
-
1779
-		foreach (['user1', 'user2', 'user3', 'user4'] as $userId) {
1780
-			$this->assertEquals(false, $userConfig->hasKey($userId, $app, $key, null));
1781
-			$userConfigTemp = $this->generateUserConfig($preload ?? []);
1782
-			$this->assertEquals(false, $userConfigTemp->hasKey($userId, $app, $key, null));
1783
-		}
1784
-	}
1785
-
1786
-	public function testDeleteApp(): void {
1787
-		$userConfig = $this->generateUserConfig();
1788
-		$userConfig->deleteApp('only-lazy');
1789
-
1790
-		foreach (['user1', 'user2', 'user3', 'user4'] as $userId) {
1791
-			$this->assertEquals(false, in_array('only-lazy', $userConfig->getApps($userId)));
1792
-			$userConfigTemp = $this->generateUserConfig();
1793
-			$this->assertEquals(false, in_array('only-lazy', $userConfigTemp->getApps($userId)));
1794
-		}
1795
-	}
1796
-
1797
-	public function testDeleteAllPreferences(): void {
1798
-		$userConfig = $this->generateUserConfig();
1799
-		$userConfig->deleteAllUserConfig('user1');
1800
-
1801
-		$this->assertEqualsCanonicalizing([], $userConfig->getApps('user1'));
1802
-		$userConfig = $this->generateUserConfig();
1803
-		$this->assertEqualsCanonicalizing([], $userConfig->getApps('user1'));
1804
-	}
1805
-
1806
-	public function testClearCache(): void {
1807
-		$userConfig = $this->generateUserConfig(['user1', 'user2']);
1808
-		$userConfig->clearCache('user1');
1809
-
1810
-		$this->assertEquals(true, $userConfig->statusCache()['fastLoaded']['user2']);
1811
-		$this->assertEquals(false, $userConfig->statusCache()['fastLoaded']['user1']);
1812
-		$this->assertEquals('value2a', $userConfig->getValueString('user1', 'app2', 'key2'));
1813
-		$this->assertEquals(false, $userConfig->statusCache()['lazyLoaded']['user1']);
1814
-		$this->assertEquals(true, $userConfig->statusCache()['fastLoaded']['user1']);
1815
-	}
1816
-
1817
-	public function testClearCacheAll(): void {
1818
-		$userConfig = $this->generateUserConfig(['user1', 'user2']);
1819
-		$userConfig->clearCacheAll();
1820
-		$this->assertEqualsCanonicalizing(
1821
-			[
1822
-				'fastLoaded' => [],
1823
-				'fastCache' => [],
1824
-				'lazyLoaded' => [],
1825
-				'lazyCache' => [],
1826
-				'valueDetails' => [],
1827
-			],
1828
-			$userConfig->statusCache()
1829
-		);
1830
-	}
29
+    protected IDBConnection $connection;
30
+    private IConfig $config;
31
+    private LoggerInterface $logger;
32
+    private ICrypto $crypto;
33
+    private array $originalPreferences;
34
+
35
+    /**
36
+     * @var array<string, array<string, array<array<string, string, int, bool, bool>>> [userId => [appId => prefKey, prefValue, valueType, lazy, sensitive]]]
37
+     */
38
+    private array $basePreferences =
39
+        [
40
+            'user1' =>
41
+                [
42
+                    'app1' => [
43
+                        'key1' => ['key1', 'value1'],
44
+                        'key22' => ['key22', '31'],
45
+                        'fast_string' => ['fast_string', 'f_value', ValueType::STRING],
46
+                        'lazy_string' => ['lazy_string', 'l_value', ValueType::STRING, true],
47
+                        'fast_string_sensitive' => [
48
+                            'fast_string_sensitive', 'fs_value', ValueType::STRING, false, UserConfig::FLAG_SENSITIVE
49
+                        ],
50
+                        'lazy_string_sensitive' => [
51
+                            'lazy_string_sensitive', 'ls_value', ValueType::STRING, true, UserConfig::FLAG_SENSITIVE
52
+                        ],
53
+                        'fast_int' => ['fast_int', 11, ValueType::INT],
54
+                        'lazy_int' => ['lazy_int', 12, ValueType::INT, true],
55
+                        'fast_int_sensitive' => ['fast_int_sensitive', 2024, ValueType::INT, false, UserConfig::FLAG_SENSITIVE],
56
+                        'lazy_int_sensitive' => ['lazy_int_sensitive', 2048, ValueType::INT, true, UserConfig::FLAG_SENSITIVE],
57
+                        'fast_float' => ['fast_float', 3.14, ValueType::FLOAT],
58
+                        'lazy_float' => ['lazy_float', 3.14159, ValueType::FLOAT, true],
59
+                        'fast_float_sensitive' => [
60
+                            'fast_float_sensitive', 1.41, ValueType::FLOAT, false, UserConfig::FLAG_SENSITIVE
61
+                        ],
62
+                        'lazy_float_sensitive' => [
63
+                            'lazy_float_sensitive', 1.4142, ValueType::FLOAT, true, UserConfig::FLAG_SENSITIVE
64
+                        ],
65
+                        'fast_array' => ['fast_array', ['year' => 2024], ValueType::ARRAY],
66
+                        'lazy_array' => ['lazy_array', ['month' => 'October'], ValueType::ARRAY, true],
67
+                        'fast_array_sensitive' => [
68
+                            'fast_array_sensitive', ['password' => 'pwd'], ValueType::ARRAY, false, UserConfig::FLAG_SENSITIVE
69
+                        ],
70
+                        'lazy_array_sensitive' => [
71
+                            'lazy_array_sensitive', ['password' => 'qwerty'], ValueType::ARRAY, true, UserConfig::FLAG_SENSITIVE
72
+                        ],
73
+                        'fast_boolean' => ['fast_boolean', true, ValueType::BOOL],
74
+                        'fast_boolean_0' => ['fast_boolean_0', false, ValueType::BOOL],
75
+                        'lazy_boolean' => ['lazy_boolean', true, ValueType::BOOL, true],
76
+                        'lazy_boolean_0' => ['lazy_boolean_0', false, ValueType::BOOL, true],
77
+                    ],
78
+                    'app2' => [
79
+                        'key2' => ['key2', 'value2a', ValueType::STRING, false, 0, true],
80
+                        'key3' => ['key3', 'value3', ValueType::STRING, true],
81
+                        'key4' => ['key4', 'value4', ValueType::STRING, false, UserConfig::FLAG_SENSITIVE],
82
+                        'key8' => ['key8', 11, ValueType::INT, false, 0, true],
83
+                        'key9' => ['key9', 'value9a', ValueType::STRING],
84
+                    ],
85
+                    'app3' => [
86
+                        'key1' => ['key1', 'value123'],
87
+                        'key3' => ['key3', 'value3'],
88
+                        'key8' => ['key8', 12, ValueType::INT, false, UserConfig::FLAG_SENSITIVE, true],
89
+                        'key9' => ['key9', 'value9b', ValueType::STRING, false, UserConfig::FLAG_SENSITIVE],
90
+                        'key10' => ['key10', true, ValueType::BOOL, false, 0, true],
91
+                    ],
92
+                    'only-lazy' => [
93
+                        'key1' => ['key1', 'value456', ValueType::STRING, true, 0, true],
94
+                        'key2' => ['key2', 'value2c', ValueType::STRING, true, UserConfig::FLAG_SENSITIVE],
95
+                        'key3' => ['key3', 42, ValueType::INT, true],
96
+                        'key4' => ['key4', 17.42, ValueType::FLOAT, true],
97
+                        'key5' => ['key5', true, ValueType::BOOL, true],
98
+                    ]
99
+                ],
100
+            'user2' =>
101
+                [
102
+                    'app1' => [
103
+                        '1' => ['1', 'value1'],
104
+                        '2' => ['2', 'value2', ValueType::STRING, true, UserConfig::FLAG_SENSITIVE],
105
+                        '3' => ['3', 17, ValueType::INT, true],
106
+                        '4' => ['4', 42, ValueType::INT, false, UserConfig::FLAG_SENSITIVE],
107
+                        '5' => ['5', 17.42, ValueType::FLOAT, false],
108
+                        '6' => ['6', true, ValueType::BOOL, false],
109
+                    ],
110
+                    'app2' => [
111
+                        'key2' => ['key2', 'value2b', ValueType::STRING, false, 0, true],
112
+                        'key3' => ['key3', 'value3', ValueType::STRING, true],
113
+                        'key4' => ['key4', 'value4', ValueType::STRING, false, UserConfig::FLAG_SENSITIVE],
114
+                        'key8' => ['key8', 12, ValueType::INT, false, 0, true],
115
+                    ],
116
+                    'app3' => [
117
+                        'key10' => ['key10', false, ValueType::BOOL, false, 0, true],
118
+                    ],
119
+                    'only-lazy' => [
120
+                        'key1' => ['key1', 'value1', ValueType::STRING, true, 0, true]
121
+                    ]
122
+                ],
123
+            'user3' =>
124
+                [
125
+                    'app2' => [
126
+                        'key2' => ['key2', 'value2c', ValueType::MIXED, false, 0, true],
127
+                        'key3' => ['key3', 'value3', ValueType::STRING, true, ],
128
+                        'key4' => ['key4', 'value4', ValueType::STRING, false, UserConfig::FLAG_SENSITIVE],
129
+                        'fast_string_sensitive' => [
130
+                            'fast_string_sensitive', 'fs_value', ValueType::STRING, false, UserConfig::FLAG_SENSITIVE
131
+                        ],
132
+                        'lazy_string_sensitive' => [
133
+                            'lazy_string_sensitive', 'ls_value', ValueType::STRING, true, UserConfig::FLAG_SENSITIVE
134
+                        ],
135
+                    ],
136
+                    'only-lazy' => [
137
+                        'key3' => ['key3', 'value3', ValueType::STRING, true]
138
+                    ]
139
+                ],
140
+            'user4' =>
141
+                [
142
+                    'app2' => [
143
+                        'key1' => ['key1', 'value1'],
144
+                        'key2' => ['key2', 'value2A', ValueType::MIXED, false, 0, true],
145
+                        'key3' => ['key3', 'value3', ValueType::STRING, true,],
146
+                        'key4' => ['key4', 'value4', ValueType::STRING, false, UserConfig::FLAG_SENSITIVE],
147
+                    ],
148
+                    'app3' => [
149
+                        'key10' => ['key10', true, ValueType::BOOL, false, 0, true],
150
+                    ],
151
+                    'only-lazy' => [
152
+                        'key1' => ['key1', 123, ValueType::INT, true, 0, true]
153
+                    ]
154
+                ],
155
+            'user5' =>
156
+                [
157
+                    'app1' => [
158
+                        'key1' => ['key1', 'value1']
159
+                    ],
160
+                    'app2' => [
161
+                        'key8' => ['key8', 12, ValueType::INT, false, 0, true]
162
+                    ],
163
+                    'only-lazy' => [
164
+                        'key1' => ['key1', 'value1', ValueType::STRING, true, 0, true]
165
+                    ]
166
+                ],
167
+
168
+        ];
169
+
170
+    protected function setUp(): void {
171
+        parent::setUp();
172
+
173
+        $this->connection = \OCP\Server::get(IDBConnection::class);
174
+        $this->config = \OCP\Server::get(IConfig::class);
175
+        $this->logger = \OCP\Server::get(LoggerInterface::class);
176
+        $this->crypto = \OCP\Server::get(ICrypto::class);
177
+
178
+        // storing current preferences and emptying the data table
179
+        $sql = $this->connection->getQueryBuilder();
180
+        $sql->select('*')
181
+            ->from('preferences');
182
+        $result = $sql->executeQuery();
183
+        $this->originalPreferences = $result->fetchAll();
184
+        $result->closeCursor();
185
+
186
+        $sql = $this->connection->getQueryBuilder();
187
+        $sql->delete('preferences');
188
+        $sql->executeStatement();
189
+
190
+        $sql = $this->connection->getQueryBuilder();
191
+        $sql->insert('preferences')
192
+            ->values(
193
+                [
194
+                    'userid' => $sql->createParameter('userid'),
195
+                    'appid' => $sql->createParameter('appid'),
196
+                    'configkey' => $sql->createParameter('configkey'),
197
+                    'configvalue' => $sql->createParameter('configvalue'),
198
+                    'type' => $sql->createParameter('type'),
199
+                    'lazy' => $sql->createParameter('lazy'),
200
+                    'flags' => $sql->createParameter('flags'),
201
+                    'indexed' => $sql->createParameter('indexed')
202
+                ]
203
+            );
204
+
205
+        foreach ($this->basePreferences as $userId => $userData) {
206
+            foreach ($userData as $appId => $appData) {
207
+                foreach ($appData as $key => $row) {
208
+                    $value = $row[1];
209
+                    $type = ($row[2] ?? ValueType::MIXED)->value;
210
+
211
+                    if ($type === ValueType::ARRAY->value) {
212
+                        $value = json_encode($value);
213
+                    }
214
+
215
+                    if ($type === ValueType::BOOL->value && $value === false) {
216
+                        $value = '0';
217
+                    }
218
+
219
+                    $flags = $row[4] ?? 0;
220
+                    if ((UserConfig::FLAG_SENSITIVE & $flags) !== 0) {
221
+                        $value = self::invokePrivate(UserConfig::class, 'ENCRYPTION_PREFIX')
222
+                                    . $this->crypto->encrypt((string)$value);
223
+                    } else {
224
+                        $indexed = (($row[5] ?? false) === true) ? $value : '';
225
+                    }
226
+
227
+                    $sql->setParameters(
228
+                        [
229
+                            'userid' => $userId,
230
+                            'appid' => $appId,
231
+                            'configkey' => $row[0],
232
+                            'configvalue' => $value,
233
+                            'type' => $type,
234
+                            'lazy' => (($row[3] ?? false) === true) ? 1 : 0,
235
+                            'flags' => $flags,
236
+                            'indexed' => $indexed ?? ''
237
+                        ]
238
+                    )->executeStatement();
239
+                }
240
+            }
241
+        }
242
+    }
243
+
244
+    protected function tearDown(): void {
245
+        $sql = $this->connection->getQueryBuilder();
246
+        $sql->delete('preferences');
247
+        $sql->executeStatement();
248
+
249
+        $sql = $this->connection->getQueryBuilder();
250
+        $sql->insert('preferences')
251
+            ->values(
252
+                [
253
+                    'userid' => $sql->createParameter('userid'),
254
+                    'appid' => $sql->createParameter('appid'),
255
+                    'configkey' => $sql->createParameter('configkey'),
256
+                    'configvalue' => $sql->createParameter('configvalue'),
257
+                    'lazy' => $sql->createParameter('lazy'),
258
+                    'type' => $sql->createParameter('type'),
259
+                ]
260
+            );
261
+
262
+        foreach ($this->originalPreferences as $key => $configs) {
263
+            $sql->setParameter('userid', $configs['userid'])
264
+                ->setParameter('appid', $configs['appid'])
265
+                ->setParameter('configkey', $configs['configkey'])
266
+                ->setParameter('configvalue', $configs['configvalue'])
267
+                ->setParameter('lazy', ($configs['lazy'] === '1') ? '1' : '0')
268
+                ->setParameter('type', $configs['type']);
269
+            $sql->executeStatement();
270
+        }
271
+
272
+        parent::tearDown();
273
+    }
274
+
275
+    /**
276
+     * @param array $preLoading preload the 'fast' cache for a list of users)
277
+     *
278
+     * @return IUserConfig
279
+     */
280
+    private function generateUserConfig(array $preLoading = []): IUserConfig {
281
+        $userConfig = new \OC\Config\UserConfig(
282
+            $this->connection,
283
+            $this->config,
284
+            $this->logger,
285
+            $this->crypto,
286
+        );
287
+        $msg = ' generateUserConfig() failed to confirm cache status';
288
+
289
+        // confirm cache status
290
+        $status = $userConfig->statusCache();
291
+        $this->assertSame([], $status['fastLoaded'], $msg);
292
+        $this->assertSame([], $status['lazyLoaded'], $msg);
293
+        $this->assertSame([], $status['fastCache'], $msg);
294
+        $this->assertSame([], $status['lazyCache'], $msg);
295
+        foreach ($preLoading as $preLoadUser) {
296
+            // simple way to initiate the load of non-lazy preferences values in cache
297
+            $userConfig->getValueString($preLoadUser, 'core', 'preload');
298
+
299
+            // confirm cache status
300
+            $status = $userConfig->statusCache();
301
+            $this->assertSame(true, $status['fastLoaded'][$preLoadUser], $msg);
302
+            $this->assertSame(false, $status['lazyLoaded'][$preLoadUser], $msg);
303
+
304
+            $apps = array_values(array_diff(array_keys($this->basePreferences[$preLoadUser]), ['only-lazy']));
305
+            $this->assertEqualsCanonicalizing($apps, array_keys($status['fastCache'][$preLoadUser]), $msg);
306
+            $this->assertSame([], array_keys($status['lazyCache'][$preLoadUser]), $msg);
307
+        }
308
+
309
+        return $userConfig;
310
+    }
311
+
312
+    public function testGetUserIdsEmpty(): void {
313
+        $userConfig = $this->generateUserConfig();
314
+        $this->assertEqualsCanonicalizing(array_keys($this->basePreferences), $userConfig->getUserIds());
315
+    }
316
+
317
+    public function testGetUserIds(): void {
318
+        $userConfig = $this->generateUserConfig();
319
+        $this->assertEqualsCanonicalizing(['user1', 'user2', 'user5'], $userConfig->getUserIds('app1'));
320
+    }
321
+
322
+    public function testGetApps(): void {
323
+        $userConfig = $this->generateUserConfig();
324
+        $this->assertEqualsCanonicalizing(
325
+            array_keys($this->basePreferences['user1']), $userConfig->getApps('user1')
326
+        );
327
+    }
328
+
329
+    public function testGetKeys(): void {
330
+        $userConfig = $this->generateUserConfig(['user1']);
331
+        $this->assertEqualsCanonicalizing(
332
+            array_keys($this->basePreferences['user1']['app1']), $userConfig->getKeys('user1', 'app1')
333
+        );
334
+    }
335
+
336
+    public static function providerHasKey(): array {
337
+        return [
338
+            ['user1', 'app1', 'key1', false, true],
339
+            ['user0', 'app1', 'key1', false, false],
340
+            ['user1', 'app1', 'key1', true, false],
341
+            ['user1', 'app1', 'key0', false, false],
342
+            ['user1', 'app1', 'key0', true, false],
343
+            ['user1', 'app1', 'fast_string_sensitive', false, true],
344
+            ['user1', 'app1', 'lazy_string_sensitive', true, true],
345
+            ['user2', 'only-lazy', 'key1', false, false],
346
+            ['user2', 'only-lazy', 'key1', true, true],
347
+        ];
348
+    }
349
+
350
+    /**
351
+     * @dataProvider providerHasKey
352
+     */
353
+    public function testHasKey(string $userId, string $appId, string $key, ?bool $lazy, bool $result): void {
354
+        $userConfig = $this->generateUserConfig();
355
+        $this->assertEquals($result, $userConfig->hasKey($userId, $appId, $key, $lazy));
356
+    }
357
+
358
+    public static function providerIsSensitive(): array {
359
+        return [
360
+            ['user1', 'app1', 'key1', false, false, false],
361
+            ['user0', 'app1', 'key1', false, false, true],
362
+            ['user1', 'app1', 'key1', true, false, true],
363
+            ['user1', 'app1', 'key1', null, false, false],
364
+            ['user1', 'app1', 'key0', false, false, true],
365
+            ['user1', 'app1', 'key0', true, false, true],
366
+            ['user1', 'app1', 'fast_string_sensitive', false, true, false],
367
+            ['user1', 'app1', 'lazy_string_sensitive', true, true, false],
368
+            ['user1', 'app1', 'fast_string_sensitive', true, true, true],
369
+            ['user1', 'app1', 'lazy_string_sensitive', false, true, true],
370
+            ['user1', 'app1', 'lazy_string_sensitive', null, true, false],
371
+            ['user2', 'only-lazy', 'key1', false, false, true],
372
+            ['user2', 'only-lazy', 'key1', true, false, false],
373
+            ['user2', 'only-lazy', 'key1', null, false, false],
374
+        ];
375
+    }
376
+
377
+    /**
378
+     * @dataProvider providerIsSensitive
379
+     */
380
+    public function testIsSensitive(
381
+        string $userId,
382
+        string $appId,
383
+        string $key,
384
+        ?bool $lazy,
385
+        bool $result,
386
+        bool $exception,
387
+    ): void {
388
+        $userConfig = $this->generateUserConfig();
389
+        if ($exception) {
390
+            $this->expectException(UnknownKeyException::class);
391
+        }
392
+
393
+        $this->assertEquals($result, $userConfig->isSensitive($userId, $appId, $key, $lazy));
394
+    }
395
+
396
+    public static function providerIsLazy(): array {
397
+        return [
398
+            ['user1', 'app1', 'key1', false, false],
399
+            ['user0', 'app1', 'key1', false, true],
400
+            ['user1', 'app1', 'key0', false, true],
401
+            ['user1', 'app1', 'key0', false, true],
402
+            ['user1', 'app1', 'fast_string_sensitive', false, false],
403
+            ['user1', 'app1', 'lazy_string_sensitive', true, false],
404
+            ['user2', 'only-lazy', 'key1', true, false],
405
+        ];
406
+    }
407
+
408
+    /**
409
+     * @dataProvider providerIsLazy
410
+     */
411
+    public function testIsLazy(
412
+        string $userId,
413
+        string $appId,
414
+        string $key,
415
+        bool $result,
416
+        bool $exception,
417
+    ): void {
418
+        $userConfig = $this->generateUserConfig();
419
+        if ($exception) {
420
+            $this->expectException(UnknownKeyException::class);
421
+        }
422
+
423
+        $this->assertEquals($result, $userConfig->isLazy($userId, $appId, $key));
424
+    }
425
+
426
+    public static function providerGetValues(): array {
427
+        return [
428
+            [
429
+                'user1', 'app1', '', true,
430
+                [
431
+                    'fast_array' => ['year' => 2024],
432
+                    'fast_array_sensitive' => '***REMOVED SENSITIVE VALUE***',
433
+                    'fast_boolean' => true,
434
+                    'fast_boolean_0' => false,
435
+                    'fast_float' => 3.14,
436
+                    'fast_float_sensitive' => '***REMOVED SENSITIVE VALUE***',
437
+                    'fast_int' => 11,
438
+                    'fast_int_sensitive' => '***REMOVED SENSITIVE VALUE***',
439
+                    'fast_string' => 'f_value',
440
+                    'fast_string_sensitive' => '***REMOVED SENSITIVE VALUE***',
441
+                    'key1' => 'value1',
442
+                    'key22' => '31',
443
+                    'lazy_array' => ['month' => 'October'],
444
+                    'lazy_array_sensitive' => '***REMOVED SENSITIVE VALUE***',
445
+                    'lazy_boolean' => true,
446
+                    'lazy_boolean_0' => false,
447
+                    'lazy_float' => 3.14159,
448
+                    'lazy_float_sensitive' => '***REMOVED SENSITIVE VALUE***',
449
+                    'lazy_int' => 12,
450
+                    'lazy_int_sensitive' => '***REMOVED SENSITIVE VALUE***',
451
+                    'lazy_string' => 'l_value',
452
+                    'lazy_string_sensitive' => '***REMOVED SENSITIVE VALUE***',
453
+                ]
454
+            ],
455
+            [
456
+                'user1', 'app1', '', false,
457
+                [
458
+                    'fast_array' => ['year' => 2024],
459
+                    'fast_array_sensitive' => ['password' => 'pwd'],
460
+                    'fast_boolean' => true,
461
+                    'fast_boolean_0' => false,
462
+                    'fast_float' => 3.14,
463
+                    'fast_float_sensitive' => 1.41,
464
+                    'fast_int' => 11,
465
+                    'fast_int_sensitive' => 2024,
466
+                    'fast_string' => 'f_value',
467
+                    'fast_string_sensitive' => 'fs_value',
468
+                    'key1' => 'value1',
469
+                    'key22' => '31',
470
+                    'lazy_array' => ['month' => 'October'],
471
+                    'lazy_array_sensitive' => ['password' => 'qwerty'],
472
+                    'lazy_boolean' => true,
473
+                    'lazy_boolean_0' => false,
474
+                    'lazy_float' => 3.14159,
475
+                    'lazy_float_sensitive' => 1.4142,
476
+                    'lazy_int' => 12,
477
+                    'lazy_int_sensitive' => 2048,
478
+                    'lazy_string' => 'l_value',
479
+                    'lazy_string_sensitive' => 'ls_value'
480
+                ]
481
+            ],
482
+            [
483
+                'user1', 'app1', 'fast_', true,
484
+                [
485
+                    'fast_array' => ['year' => 2024],
486
+                    'fast_array_sensitive' => '***REMOVED SENSITIVE VALUE***',
487
+                    'fast_boolean' => true,
488
+                    'fast_boolean_0' => false,
489
+                    'fast_float' => 3.14,
490
+                    'fast_float_sensitive' => '***REMOVED SENSITIVE VALUE***',
491
+                    'fast_int' => 11,
492
+                    'fast_int_sensitive' => '***REMOVED SENSITIVE VALUE***',
493
+                    'fast_string' => 'f_value',
494
+                    'fast_string_sensitive' => '***REMOVED SENSITIVE VALUE***',
495
+                ]
496
+            ],
497
+            [
498
+                'user1', 'app1', 'fast_', false,
499
+                [
500
+                    'fast_array' => ['year' => 2024],
501
+                    'fast_array_sensitive' => ['password' => 'pwd'],
502
+                    'fast_boolean' => true,
503
+                    'fast_boolean_0' => false,
504
+                    'fast_float' => 3.14,
505
+                    'fast_float_sensitive' => 1.41,
506
+                    'fast_int' => 11,
507
+                    'fast_int_sensitive' => 2024,
508
+                    'fast_string' => 'f_value',
509
+                    'fast_string_sensitive' => 'fs_value',
510
+                ]
511
+            ],
512
+            [
513
+                'user1', 'app1', 'key1', true,
514
+                [
515
+                    'key1' => 'value1',
516
+                ]
517
+            ],
518
+            [
519
+                'user2', 'app1', '', false,
520
+                [
521
+                    '1' => 'value1',
522
+                    '4' => 42,
523
+                    '5' => 17.42,
524
+                    '6' => true,
525
+                    '2' => 'value2',
526
+                    '3' => 17,
527
+                ]
528
+            ],
529
+            [
530
+                'user2', 'app1', '', true,
531
+                [
532
+                    '1' => 'value1',
533
+                    '4' => '***REMOVED SENSITIVE VALUE***',
534
+                    '5' => 17.42,
535
+                    '6' => true,
536
+                    '2' => '***REMOVED SENSITIVE VALUE***',
537
+                    '3' => 17,
538
+                ]
539
+            ],
540
+        ];
541
+    }
542
+
543
+    /**
544
+     * @dataProvider providerGetValues
545
+     */
546
+    public function testGetValues(
547
+        string $userId,
548
+        string $appId,
549
+        string $prefix,
550
+        bool $filtered,
551
+        array $result,
552
+    ): void {
553
+        $userConfig = $this->generateUserConfig();
554
+        $this->assertJsonStringEqualsJsonString(
555
+            json_encode($result), json_encode($userConfig->getValues($userId, $appId, $prefix, $filtered))
556
+        );
557
+    }
558
+
559
+    public static function providerGetAllValues(): array {
560
+        return [
561
+            [
562
+                'user2', false,
563
+                [
564
+                    'app1' => [
565
+                        '1' => 'value1',
566
+                        '4' => 42,
567
+                        '5' => 17.42,
568
+                        '6' => true,
569
+                        '2' => 'value2',
570
+                        '3' => 17,
571
+                    ],
572
+                    'app2' => [
573
+                        'key2' => 'value2b',
574
+                        'key3' => 'value3',
575
+                        'key4' => 'value4',
576
+                        'key8' => 12,
577
+                    ],
578
+                    'app3' => [
579
+                        'key10' => false,
580
+                    ],
581
+                    'only-lazy' => [
582
+                        'key1' => 'value1',
583
+                    ]
584
+                ],
585
+            ],
586
+            [
587
+                'user2', true,
588
+                [
589
+                    'app1' => [
590
+                        '1' => 'value1',
591
+                        '4' => '***REMOVED SENSITIVE VALUE***',
592
+                        '5' => 17.42,
593
+                        '6' => true,
594
+                        '2' => '***REMOVED SENSITIVE VALUE***',
595
+                        '3' => 17,
596
+                    ],
597
+                    'app2' => [
598
+                        'key2' => 'value2b',
599
+                        'key3' => 'value3',
600
+                        'key4' => '***REMOVED SENSITIVE VALUE***',
601
+                        'key8' => 12,
602
+                    ],
603
+                    'app3' => [
604
+                        'key10' => false,
605
+                    ],
606
+                    'only-lazy' => [
607
+                        'key1' => 'value1',
608
+                    ]
609
+                ],
610
+            ],
611
+            [
612
+                'user3', true,
613
+                [
614
+                    'app2' => [
615
+                        'key2' => 'value2c',
616
+                        'key3' => 'value3',
617
+                        'key4' => '***REMOVED SENSITIVE VALUE***',
618
+                        'fast_string_sensitive' => '***REMOVED SENSITIVE VALUE***',
619
+                        'lazy_string_sensitive' => '***REMOVED SENSITIVE VALUE***',
620
+                    ],
621
+                    'only-lazy' => [
622
+                        'key3' => 'value3',
623
+                    ]
624
+                ],
625
+            ],
626
+            [
627
+                'user3', false,
628
+                [
629
+                    'app2' => [
630
+                        'key2' => 'value2c',
631
+                        'key3' => 'value3',
632
+                        'key4' => 'value4',
633
+                        'fast_string_sensitive' => 'fs_value',
634
+                        'lazy_string_sensitive' => 'ls_value',
635
+                    ],
636
+                    'only-lazy' => [
637
+                        'key3' => 'value3',
638
+                    ]
639
+                ],
640
+            ],
641
+        ];
642
+    }
643
+
644
+    /**
645
+     * @dataProvider providerGetAllValues
646
+     */
647
+    public function testGetAllValues(
648
+        string $userId,
649
+        bool $filtered,
650
+        array $result,
651
+    ): void {
652
+        $userConfig = $this->generateUserConfig();
653
+        $this->assertEqualsCanonicalizing($result, $userConfig->getAllValues($userId, $filtered));
654
+    }
655
+
656
+    public static function providerSearchValuesByApps(): array {
657
+        return [
658
+            [
659
+                'user1', 'key1', false, null,
660
+                [
661
+                    'app1' => 'value1',
662
+                    'app3' => 'value123'
663
+                ]
664
+            ],
665
+            [
666
+                'user1', 'key1', true, null,
667
+                [
668
+                    'only-lazy' => 'value456'
669
+                ]
670
+            ],
671
+            [
672
+                'user1', 'key8', false, null,
673
+                [
674
+                    'app2' => 11,
675
+                    'app3' => 12,
676
+                ]
677
+            ],
678
+            [
679
+                'user1', 'key9', false, ValueType::INT,
680
+                [
681
+                    'app2' => 0,
682
+                    'app3' => 0,
683
+                ]
684
+            ]
685
+        ];
686
+    }
687
+
688
+    /**
689
+     * @dataProvider providerSearchValuesByApps
690
+     */
691
+    public function testSearchValuesByApps(
692
+        string $userId,
693
+        string $key,
694
+        bool $lazy,
695
+        ?ValueType $typedAs,
696
+        array $result,
697
+    ): void {
698
+        $userConfig = $this->generateUserConfig();
699
+        $this->assertEquals($result, $userConfig->getValuesByApps($userId, $key, $lazy, $typedAs));
700
+    }
701
+
702
+    public static function providerSearchValuesByUsers(): array {
703
+        return [
704
+            [
705
+                'app2', 'key2', null, null,
706
+                [
707
+                    'user1' => 'value2a',
708
+                    'user2' => 'value2b',
709
+                    'user3' => 'value2c',
710
+                    'user4' => 'value2A'
711
+                ]
712
+            ],
713
+            [
714
+                'app2', 'key2', null, ['user1', 'user3'],
715
+                [
716
+                    'user1' => 'value2a',
717
+                    'user3' => 'value2c',
718
+                ]
719
+            ],
720
+            [
721
+                'app2', 'key2', ValueType::INT, ['user1', 'user3'],
722
+                [
723
+                    'user1' => 0,
724
+                    'user3' => 0,
725
+                ]
726
+            ],
727
+            [
728
+                'app2', 'key8', ValueType::INT, null,
729
+                [
730
+                    'user1' => 11,
731
+                    'user2' => 12,
732
+                    'user5' => 12,
733
+                ]
734
+            ],
735
+        ];
736
+    }
737
+
738
+    /**
739
+     * @dataProvider providerSearchValuesByUsers
740
+     */
741
+    public function testSearchValuesByUsers(
742
+        string $app,
743
+        string $key,
744
+        ?ValueType $typedAs,
745
+        ?array $userIds,
746
+        array $result,
747
+    ): void {
748
+        $userConfig = $this->generateUserConfig();
749
+        $this->assertEqualsCanonicalizing(
750
+            $result, $userConfig->getValuesByUsers($app, $key, $typedAs, $userIds)
751
+        );
752
+    }
753
+
754
+    public static function providerSearchValuesByValueString(): array {
755
+        return [
756
+            ['app2', 'key2', 'value2a', false, ['user1']],
757
+            ['app2', 'key2', 'value2A', false, ['user4']],
758
+            ['app2', 'key2', 'value2A', true, ['user1', 'user4']]
759
+        ];
760
+    }
761
+
762
+    /**
763
+     * @dataProvider providerSearchValuesByValueString
764
+     */
765
+    public function testSearchUsersByValueString(
766
+        string $app,
767
+        string $key,
768
+        string|array $value,
769
+        bool $ci,
770
+        array $result,
771
+    ): void {
772
+        $userConfig = $this->generateUserConfig();
773
+        $this->assertEqualsCanonicalizing($result, iterator_to_array($userConfig->searchUsersByValueString($app, $key, $value, $ci)));
774
+    }
775
+
776
+    public static function providerSearchValuesByValueInt(): array {
777
+        return [
778
+            ['app3', 'key8', 12, []], // sensitive value, cannot search
779
+            ['app2', 'key8', 12, ['user2', 'user5']], // sensitive value, cannot search
780
+            ['only-lazy', 'key1', 123, ['user4']],
781
+        ];
782
+    }
783
+
784
+    /**
785
+     * @dataProvider providerSearchValuesByValueInt
786
+     */
787
+    public function testSearchUsersByValueInt(
788
+        string $app,
789
+        string $key,
790
+        int $value,
791
+        array $result,
792
+    ): void {
793
+        $userConfig = $this->generateUserConfig();
794
+        $this->assertEqualsCanonicalizing($result, iterator_to_array($userConfig->searchUsersByValueInt($app, $key, $value)));
795
+    }
796
+
797
+    public static function providerSearchValuesByValues(): array {
798
+        return [
799
+            ['app2', 'key2', ['value2a', 'value2b'], ['user1', 'user2']],
800
+            ['app2', 'key2', ['value2a', 'value2c'], ['user1', 'user3']],
801
+        ];
802
+    }
803
+
804
+    /**
805
+     * @dataProvider providerSearchValuesByValues
806
+     */
807
+    public function testSearchUsersByValues(
808
+        string $app,
809
+        string $key,
810
+        array $values,
811
+        array $result,
812
+    ): void {
813
+        $userConfig = $this->generateUserConfig();
814
+        $this->assertEqualsCanonicalizing($result, iterator_to_array($userConfig->searchUsersByValues($app, $key, $values)));
815
+    }
816
+
817
+    public static function providerSearchValuesByValueBool(): array {
818
+        return [
819
+            ['app3', 'key10', true, ['user1', 'user4']],
820
+            ['app3', 'key10', false, ['user2']],
821
+        ];
822
+    }
823
+
824
+    /**
825
+     * @dataProvider providerSearchValuesByValueBool
826
+     */
827
+    public function testSearchUsersByValueBool(
828
+        string $app,
829
+        string $key,
830
+        bool $value,
831
+        array $result,
832
+    ): void {
833
+        $userConfig = $this->generateUserConfig();
834
+        $this->assertEqualsCanonicalizing($result, iterator_to_array($userConfig->searchUsersByValueBool($app, $key, $value)));
835
+    }
836
+
837
+    public static function providerGetValueMixed(): array {
838
+        return [
839
+            [
840
+                ['user1'], 'user1', 'app1', 'key0', 'default_because_unknown_key', true,
841
+                'default_because_unknown_key'
842
+            ],
843
+            [
844
+                null, 'user1', 'app1', 'key0', 'default_because_unknown_key', true,
845
+                'default_because_unknown_key'
846
+            ],
847
+            [
848
+                ['user1'], 'user1', 'app1', 'key0', 'default_because_unknown_key', false,
849
+                'default_because_unknown_key'
850
+            ],
851
+            [
852
+                null, 'user1', 'app1', 'key0', 'default_because_unknown_key', false,
853
+                'default_because_unknown_key'
854
+            ],
855
+            [['user1'], 'user1', 'app1', 'fast_string', 'default_because_unknown_key', false, 'f_value'],
856
+            [null, 'user1', 'app1', 'fast_string', 'default_because_unknown_key', false, 'f_value'],
857
+            [['user1'], 'user1', 'app1', 'fast_string', 'default_because_unknown_key', true, 'f_value'],
858
+            // because non-lazy are already loaded
859
+            [
860
+                null, 'user1', 'app1', 'fast_string', 'default_because_unknown_key', true,
861
+                'default_because_unknown_key'
862
+            ],
863
+            [
864
+                ['user1'], 'user1', 'app1', 'lazy_string', 'default_because_unknown_key', false,
865
+                'default_because_unknown_key'
866
+            ],
867
+            [
868
+                null, 'user1', 'app1', 'lazy_string', 'default_because_unknown_key', false,
869
+                'default_because_unknown_key'
870
+            ],
871
+            [['user1'], 'user1', 'app1', 'lazy_string', 'default_because_unknown_key', true, 'l_value'],
872
+            [null, 'user1', 'app1', 'lazy_string', 'default_because_unknown_key', true, 'l_value'],
873
+            [
874
+                ['user1'], 'user1', 'app1', 'fast_string_sensitive', 'default_because_unknown_key', false,
875
+                'fs_value'
876
+            ],
877
+            [
878
+                null, 'user1', 'app1', 'fast_string_sensitive', 'default_because_unknown_key', false,
879
+                'fs_value'
880
+            ],
881
+            [
882
+                ['user1'], 'user1', 'app1', 'fast_string_sensitive', 'default_because_unknown_key', true,
883
+                'fs_value'
884
+            ],
885
+            [
886
+                null, 'user1', 'app1', 'fast_string_sensitive', 'default_because_unknown_key', true,
887
+                'default_because_unknown_key'
888
+            ],
889
+            [
890
+                ['user1'], 'user1', 'app1', 'lazy_string_sensitive', 'default_because_unknown_key', false,
891
+                'default_because_unknown_key'
892
+            ],
893
+            [
894
+                null, 'user1', 'app1', 'lazy_string_sensitive', 'default_because_unknown_key', false,
895
+                'default_because_unknown_key'
896
+            ],
897
+            [
898
+                ['user1'], 'user1', 'app1', 'lazy_string_sensitive', 'default_because_unknown_key', true,
899
+                'ls_value'
900
+            ],
901
+            [null, 'user1', 'app1', 'lazy_string_sensitive', 'default_because_unknown_key', true, 'ls_value'],
902
+        ];
903
+    }
904
+
905
+    /**
906
+     * @dataProvider providerGetValueMixed
907
+     */
908
+    public function testGetValueMixed(
909
+        ?array $preload,
910
+        string $userId,
911
+        string $app,
912
+        string $key,
913
+        string $default,
914
+        bool $lazy,
915
+        string $result,
916
+    ): void {
917
+        $userConfig = $this->generateUserConfig($preload ?? []);
918
+        $this->assertEquals($result, $userConfig->getValueMixed($userId, $app, $key, $default, $lazy));
919
+    }
920
+
921
+    /**
922
+     * @dataProvider providerGetValueMixed
923
+     */
924
+    public function testGetValueString(
925
+        ?array $preload,
926
+        string $userId,
927
+        string $app,
928
+        string $key,
929
+        string $default,
930
+        bool $lazy,
931
+        string $result,
932
+    ): void {
933
+        $userConfig = $this->generateUserConfig($preload ?? []);
934
+        $this->assertEquals($result, $userConfig->getValueString($userId, $app, $key, $default, $lazy));
935
+    }
936
+
937
+    public static function providerGetValueInt(): array {
938
+        return [
939
+            [['user1'], 'user1', 'app1', 'key0', 54321, true, 54321],
940
+            [null, 'user1', 'app1', 'key0', 54321, true, 54321],
941
+            [['user1'], 'user1', 'app1', 'key0', 54321, false, 54321],
942
+            [null, 'user1', 'app1', 'key0', 54321, false, 54321],
943
+            [null, 'user1', 'app1', 'key22', 54321, false, 31],
944
+            [['user1'], 'user1', 'app1', 'fast_int', 54321, false, 11],
945
+            [null, 'user1', 'app1', 'fast_int', 54321, false, 11],
946
+            [['user1'], 'user1', 'app1', 'fast_int', 54321, true, 11],
947
+            [null, 'user1', 'app1', 'fast_int', 54321, true, 54321],
948
+            [['user1'], 'user1', 'app1', 'fast_int_sensitive', 54321, false, 2024],
949
+            [null, 'user1', 'app1', 'fast_int_sensitive', 54321, false, 2024],
950
+            [['user1'], 'user1', 'app1', 'fast_int_sensitive', 54321, true, 2024],
951
+            [null, 'user1', 'app1', 'fast_int_sensitive', 54321, true, 54321],
952
+            [['user1'], 'user1', 'app1', 'lazy_int', 54321, false, 54321],
953
+            [null, 'user1', 'app1', 'lazy_int', 54321, false, 54321],
954
+            [['user1'], 'user1', 'app1', 'lazy_int', 54321, true, 12],
955
+            [null, 'user1', 'app1', 'lazy_int', 54321, true, 12],
956
+            [['user1'], 'user1', 'app1', 'lazy_int_sensitive', 54321, false, 54321],
957
+            [null, 'user1', 'app1', 'lazy_int_sensitive', 54321, false, 54321],
958
+            [['user1'], 'user1', 'app1', 'lazy_int_sensitive', 54321, true, 2048],
959
+            [null, 'user1', 'app1', 'lazy_int_sensitive', 54321, true, 2048],
960
+        ];
961
+    }
962
+
963
+    /**
964
+     * @dataProvider providerGetValueInt
965
+     */
966
+    public function testGetValueInt(
967
+        ?array $preload,
968
+        string $userId,
969
+        string $app,
970
+        string $key,
971
+        int $default,
972
+        bool $lazy,
973
+        int $result,
974
+    ): void {
975
+        $userConfig = $this->generateUserConfig($preload ?? []);
976
+        $this->assertEquals($result, $userConfig->getValueInt($userId, $app, $key, $default, $lazy));
977
+    }
978
+
979
+    public static function providerGetValueFloat(): array {
980
+        return [
981
+            [['user1'], 'user1', 'app1', 'key0', 54.321, true, 54.321],
982
+            [null, 'user1', 'app1', 'key0', 54.321, true, 54.321],
983
+            [['user1'], 'user1', 'app1', 'key0', 54.321, false, 54.321],
984
+            [null, 'user1', 'app1', 'key0', 54.321, false, 54.321],
985
+            [['user1'], 'user1', 'app1', 'fast_float', 54.321, false, 3.14],
986
+            [null, 'user1', 'app1', 'fast_float', 54.321, false, 3.14],
987
+            [['user1'], 'user1', 'app1', 'fast_float', 54.321, true, 3.14],
988
+            [null, 'user1', 'app1', 'fast_float', 54.321, true, 54.321],
989
+            [['user1'], 'user1', 'app1', 'fast_float_sensitive', 54.321, false, 1.41],
990
+            [null, 'user1', 'app1', 'fast_float_sensitive', 54.321, false, 1.41],
991
+            [['user1'], 'user1', 'app1', 'fast_float_sensitive', 54.321, true, 1.41],
992
+            [null, 'user1', 'app1', 'fast_float_sensitive', 54.321, true, 54.321],
993
+            [['user1'], 'user1', 'app1', 'lazy_float', 54.321, false, 54.321],
994
+            [null, 'user1', 'app1', 'lazy_float', 54.321, false, 54.321],
995
+            [['user1'], 'user1', 'app1', 'lazy_float', 54.321, true, 3.14159],
996
+            [null, 'user1', 'app1', 'lazy_float', 54.321, true, 3.14159],
997
+            [['user1'], 'user1', 'app1', 'lazy_float_sensitive', 54.321, false, 54.321],
998
+            [null, 'user1', 'app1', 'lazy_float_sensitive', 54.321, false, 54.321],
999
+            [['user1'], 'user1', 'app1', 'lazy_float_sensitive', 54.321, true, 1.4142],
1000
+            [null, 'user1', 'app1', 'lazy_float_sensitive', 54.321, true, 1.4142],
1001
+        ];
1002
+    }
1003
+
1004
+    /**
1005
+     * @dataProvider providerGetValueFloat
1006
+     */
1007
+    public function testGetValueFloat(
1008
+        ?array $preload,
1009
+        string $userId,
1010
+        string $app,
1011
+        string $key,
1012
+        float $default,
1013
+        bool $lazy,
1014
+        float $result,
1015
+    ): void {
1016
+        $userConfig = $this->generateUserConfig($preload ?? []);
1017
+        $this->assertEquals($result, $userConfig->getValueFloat($userId, $app, $key, $default, $lazy));
1018
+    }
1019
+
1020
+    public static function providerGetValueBool(): array {
1021
+        return [
1022
+            [['user1'], 'user1', 'app1', 'key0', false, true, false],
1023
+            [null, 'user1', 'app1', 'key0', false, true, false],
1024
+            [['user1'], 'user1', 'app1', 'key0', true, true, true],
1025
+            [null, 'user1', 'app1', 'key0', true, true, true],
1026
+            [['user1'], 'user1', 'app1', 'key0', false, false, false],
1027
+            [null, 'user1', 'app1', 'key0', false, false, false],
1028
+            [['user1'], 'user1', 'app1', 'key0', true, false, true],
1029
+            [null, 'user1', 'app1', 'key0', true, false, true],
1030
+            [['user1'], 'user1', 'app1', 'fast_boolean', false, false, true],
1031
+            [null, 'user1', 'app1', 'fast_boolean', false, false, true],
1032
+            [['user1'], 'user1', 'app1', 'fast_boolean_0', false, false, false],
1033
+            [null, 'user1', 'app1', 'fast_boolean_0', false, false, false],
1034
+            [['user1'], 'user1', 'app1', 'fast_boolean', true, false, true],
1035
+            [null, 'user1', 'app1', 'fast_boolean', true, false, true],
1036
+            [['user1'], 'user1', 'app1', 'fast_boolean_0', true, false, false],
1037
+            [null, 'user1', 'app1', 'fast_boolean_0', true, false, false],
1038
+            [['user1'], 'user1', 'app1', 'fast_boolean', false, true, true],
1039
+            [null, 'user1', 'app1', 'fast_boolean', false, true, false],
1040
+            [['user1'], 'user1', 'app1', 'fast_boolean_0', false, true, false],
1041
+            [null, 'user1', 'app1', 'fast_boolean_0', false, true, false],
1042
+            [['user1'], 'user1', 'app1', 'fast_boolean', true, true, true],
1043
+            [null, 'user1', 'app1', 'fast_boolean', true, true, true],
1044
+            [['user1'], 'user1', 'app1', 'fast_boolean_0', true, true, false],
1045
+            [null, 'user1', 'app1', 'fast_boolean_0', true, true, true],
1046
+            [['user1'], 'user1', 'app1', 'lazy_boolean', false, false, false],
1047
+            [null, 'user1', 'app1', 'lazy_boolean', false, false, false],
1048
+            [['user1'], 'user1', 'app1', 'lazy_boolean_0', false, false, false],
1049
+            [null, 'user1', 'app1', 'lazy_boolean_0', false, false, false],
1050
+            [['user1'], 'user1', 'app1', 'lazy_boolean', true, false, true],
1051
+            [null, 'user1', 'app1', 'lazy_boolean', true, false, true],
1052
+            [['user1'], 'user1', 'app1', 'lazy_boolean_0', true, false, true],
1053
+            [null, 'user1', 'app1', 'lazy_boolean_0', true, false, true],
1054
+            [['user1'], 'user1', 'app1', 'lazy_boolean', false, true, true],
1055
+            [null, 'user1', 'app1', 'lazy_boolean', false, true, true],
1056
+            [['user1'], 'user1', 'app1', 'lazy_boolean_0', false, true, false],
1057
+            [null, 'user1', 'app1', 'lazy_boolean_0', false, true, false],
1058
+            [['user1'], 'user1', 'app1', 'lazy_boolean', true, true, true],
1059
+            [null, 'user1', 'app1', 'lazy_boolean', true, true, true],
1060
+            [['user1'], 'user1', 'app1', 'lazy_boolean_0', true, true, false],
1061
+            [null, 'user1', 'app1', 'lazy_boolean_0', true, true, false],
1062
+        ];
1063
+    }
1064
+
1065
+    /**
1066
+     * @dataProvider providerGetValueBool
1067
+     */
1068
+    public function testGetValueBool(
1069
+        ?array $preload,
1070
+        string $userId,
1071
+        string $app,
1072
+        string $key,
1073
+        bool $default,
1074
+        bool $lazy,
1075
+        bool $result,
1076
+    ): void {
1077
+        $userConfig = $this->generateUserConfig($preload ?? []);
1078
+        $this->assertEquals($result, $userConfig->getValueBool($userId, $app, $key, $default, $lazy));
1079
+    }
1080
+
1081
+    public static function providerGetValueArray(): array {
1082
+        return [
1083
+            [
1084
+                ['user1'], 'user1', 'app1', 'key0', ['default_because_unknown_key'], true,
1085
+                ['default_because_unknown_key']
1086
+            ],
1087
+            [
1088
+                null, 'user1', 'app1', 'key0', ['default_because_unknown_key'], true,
1089
+                ['default_because_unknown_key']
1090
+            ],
1091
+            [
1092
+                ['user1'], 'user1', 'app1', 'key0', ['default_because_unknown_key'], false,
1093
+                ['default_because_unknown_key']
1094
+            ],
1095
+            [
1096
+                null, 'user1', 'app1', 'key0', ['default_because_unknown_key'], false,
1097
+                ['default_because_unknown_key']
1098
+            ],
1099
+        ];
1100
+    }
1101
+
1102
+    /**
1103
+     * @dataProvider providerGetValueArray
1104
+     */
1105
+    public function testGetValueArray(
1106
+        ?array $preload,
1107
+        string $userId,
1108
+        string $app,
1109
+        string $key,
1110
+        array $default,
1111
+        bool $lazy,
1112
+        array $result,
1113
+    ): void {
1114
+        $userConfig = $this->generateUserConfig($preload ?? []);
1115
+        $this->assertEqualsCanonicalizing(
1116
+            $result, $userConfig->getValueArray($userId, $app, $key, $default, $lazy)
1117
+        );
1118
+    }
1119
+
1120
+    public static function providerGetValueType(): array {
1121
+        return [
1122
+            [null, 'user1', 'app1', 'key1', false, ValueType::MIXED],
1123
+            [null, 'user1', 'app1', 'key1', true, null, UnknownKeyException::class],
1124
+            [null, 'user1', 'app1', 'fast_string', true, ValueType::STRING, UnknownKeyException::class],
1125
+            [['user1'], 'user1', 'app1', 'fast_string', true, ValueType::STRING],
1126
+            [null, 'user1', 'app1', 'fast_string', false, ValueType::STRING],
1127
+            [null, 'user1', 'app1', 'lazy_string', true, ValueType::STRING],
1128
+            [null, 'user1', 'app1', 'lazy_string', false, ValueType::STRING, UnknownKeyException::class],
1129
+            [
1130
+                null, 'user1', 'app1', 'fast_string_sensitive', true, ValueType::STRING,
1131
+                UnknownKeyException::class
1132
+            ],
1133
+            [['user1'], 'user1', 'app1', 'fast_string_sensitive', true, ValueType::STRING],
1134
+            [null, 'user1', 'app1', 'fast_string_sensitive', false, ValueType::STRING],
1135
+            [null, 'user1', 'app1', 'lazy_string_sensitive', true, ValueType::STRING],
1136
+            [
1137
+                null, 'user1', 'app1', 'lazy_string_sensitive', false, ValueType::STRING,
1138
+                UnknownKeyException::class
1139
+            ],
1140
+            [null, 'user1', 'app1', 'fast_int', true, ValueType::INT, UnknownKeyException::class],
1141
+            [['user1'], 'user1', 'app1', 'fast_int', true, ValueType::INT],
1142
+            [null, 'user1', 'app1', 'fast_int', false, ValueType::INT],
1143
+            [null, 'user1', 'app1', 'lazy_int', true, ValueType::INT],
1144
+            [null, 'user1', 'app1', 'lazy_int', false, ValueType::INT, UnknownKeyException::class],
1145
+            [null, 'user1', 'app1', 'fast_float', true, ValueType::FLOAT, UnknownKeyException::class],
1146
+            [['user1'], 'user1', 'app1', 'fast_float', true, ValueType::FLOAT],
1147
+            [null, 'user1', 'app1', 'fast_float', false, ValueType::FLOAT],
1148
+            [null, 'user1', 'app1', 'lazy_float', true, ValueType::FLOAT],
1149
+            [null, 'user1', 'app1', 'lazy_float', false, ValueType::FLOAT, UnknownKeyException::class],
1150
+            [null, 'user1', 'app1', 'fast_boolean', true, ValueType::BOOL, UnknownKeyException::class],
1151
+            [['user1'], 'user1', 'app1', 'fast_boolean', true, ValueType::BOOL],
1152
+            [null, 'user1', 'app1', 'fast_boolean', false, ValueType::BOOL],
1153
+            [null, 'user1', 'app1', 'lazy_boolean', true, ValueType::BOOL],
1154
+            [null, 'user1', 'app1', 'lazy_boolean', false, ValueType::BOOL, UnknownKeyException::class],
1155
+        ];
1156
+    }
1157
+
1158
+    /**
1159
+     * @dataProvider providerGetValueType
1160
+     */
1161
+    public function testGetValueType(
1162
+        ?array $preload,
1163
+        string $userId,
1164
+        string $app,
1165
+        string $key,
1166
+        ?bool $lazy,
1167
+        ?ValueType $result,
1168
+        ?string $exception = null,
1169
+    ): void {
1170
+        $userConfig = $this->generateUserConfig($preload ?? []);
1171
+        if ($exception !== null) {
1172
+            $this->expectException($exception);
1173
+        }
1174
+
1175
+        $type = $userConfig->getValueType($userId, $app, $key, $lazy);
1176
+        if ($exception === null) {
1177
+            $this->assertEquals($result->value, $type->value);
1178
+        }
1179
+    }
1180
+
1181
+    public static function providerSetValueMixed(): array {
1182
+        return [
1183
+            [null, 'user1', 'app1', 'key1', 'value', false, false, true],
1184
+            [null, 'user1', 'app1', 'key1', '12345', true, false, true],
1185
+            [null, 'user1', 'app1', 'key1', '12345', true, true, true],
1186
+            [null, 'user1', 'app1', 'key1', 'value1', false, false, false],
1187
+            [null, 'user1', 'app1', 'key1', 'value1', true, false, true],
1188
+            [null, 'user1', 'app1', 'key1', 'value1', false, true, true],
1189
+            [
1190
+                null, 'user1', 'app1', 'fast_string', 'f_value_2', false, false, true,
1191
+                TypeConflictException::class
1192
+            ],
1193
+            [
1194
+                null, 'user1', 'app1', 'fast_string', 'f_value', true, false, true,
1195
+                TypeConflictException::class
1196
+            ],
1197
+            [null, 'user1', 'app1', 'fast_string', 'f_value', true, true, true, TypeConflictException::class],
1198
+            [null, 'user1', 'app1', 'fast_int', 'n_value', false, false, true, TypeConflictException::class],
1199
+            [
1200
+                null, 'user1', 'app1', 'fast_float', 'n_value', false, false, true,
1201
+                TypeConflictException::class
1202
+            ],
1203
+            [
1204
+                null, 'user1', 'app1', 'lazy_string', 'l_value_2', false, false, true,
1205
+                TypeConflictException::class
1206
+            ],
1207
+            [null, 'user1', 'app1', 'lazy_string', 'l_value', true, false, false],
1208
+            [null, 'user1', 'app1', 'lazy_string', 'l_value', true, true, true, TypeConflictException::class],
1209
+            [null, 'user1', 'app1', 'lazy_int', 'l_value', false, false, true, TypeConflictException::class],
1210
+            [
1211
+                null, 'user1', 'app1', 'lazy_float', 'l_value', false, false, true,
1212
+                TypeConflictException::class
1213
+            ],
1214
+        ];
1215
+    }
1216
+
1217
+    /**
1218
+     * @dataProvider providerSetValueMixed
1219
+     */
1220
+    public function testSetValueMixed(
1221
+        ?array $preload,
1222
+        string $userId,
1223
+        string $app,
1224
+        string $key,
1225
+        string $value,
1226
+        bool $lazy,
1227
+        bool $sensitive,
1228
+        bool $result,
1229
+        ?string $exception = null,
1230
+    ): void {
1231
+        $userConfig = $this->generateUserConfig($preload ?? []);
1232
+        if ($exception !== null) {
1233
+            $this->expectException($exception);
1234
+        }
1235
+
1236
+        $edited = $userConfig->setValueMixed($userId, $app, $key, $value, $lazy, ($sensitive) ? 1 : 0);
1237
+
1238
+        if ($exception === null) {
1239
+            $this->assertEquals($result, $edited);
1240
+        }
1241
+    }
1242
+
1243
+
1244
+    public static function providerSetValueString(): array {
1245
+        return [
1246
+            [null, 'user1', 'app1', 'key1', 'value', false, false, true],
1247
+            [null, 'user1', 'app1', 'key1', '12345', true, false, true],
1248
+            [null, 'user1', 'app1', 'key1', '12345', true, true, true],
1249
+            [null, 'user1', 'app1', 'key1', 'value1', false, false, false],
1250
+            [null, 'user1', 'app1', 'key1', 'value1', true, false, true],
1251
+            [null, 'user1', 'app1', 'key1', 'value1', false, true, true],
1252
+            [null, 'user1', 'app1', 'fast_string', 'f_value_2', false, false, true],
1253
+            [null, 'user1', 'app1', 'fast_string', 'f_value', false, false, false],
1254
+            [null, 'user1', 'app1', 'fast_string', 'f_value', true, false, true],
1255
+            [null, 'user1', 'app1', 'fast_string', 'f_value', true, true, true],
1256
+            [null, 'user1', 'app1', 'lazy_string', 'l_value_2', false, false, true],
1257
+            [null, 'user1', 'app1', 'lazy_string', 'l_value', true, false, false],
1258
+            [null, 'user1', 'app1', 'lazy_string', 'l_value', true, true, true],
1259
+            [null, 'user1', 'app1', 'fast_string_sensitive', 'fs_value', false, true, false],
1260
+            [null, 'user1', 'app1', 'fast_string_sensitive', 'fs_value', true, true, true],
1261
+            [null, 'user1', 'app1', 'fast_string_sensitive', 'fs_value', true, false, true],
1262
+            [null, 'user1', 'app1', 'lazy_string_sensitive', 'ls_value', false, true, true],
1263
+            [null, 'user1', 'app1', 'lazy_string_sensitive', 'ls_value', true, true, false],
1264
+            [null, 'user1', 'app1', 'lazy_string_sensitive', 'ls_value', true, false, false],
1265
+            [null, 'user1', 'app1', 'lazy_string_sensitive', 'ls_value_2', true, false, true],
1266
+            [null, 'user1', 'app1', 'fast_int', 'n_value', false, false, true, TypeConflictException::class],
1267
+            [
1268
+                null, 'user1', 'app1', 'fast_float', 'n_value', false, false, true,
1269
+                TypeConflictException::class
1270
+            ],
1271
+            [
1272
+                null, 'user1', 'app1', 'fast_float', 'n_value', false, false, true,
1273
+                TypeConflictException::class
1274
+            ],
1275
+            [null, 'user1', 'app1', 'lazy_int', 'n_value', false, false, true, TypeConflictException::class],
1276
+            [
1277
+                null, 'user1', 'app1', 'lazy_boolean', 'n_value', false, false, true,
1278
+                TypeConflictException::class
1279
+            ],
1280
+            [
1281
+                null, 'user1', 'app1', 'lazy_float', 'n_value', false, false, true,
1282
+                TypeConflictException::class
1283
+            ],
1284
+        ];
1285
+    }
1286
+
1287
+    /**
1288
+     * @dataProvider providerSetValueString
1289
+     */
1290
+    public function testSetValueString(
1291
+        ?array $preload,
1292
+        string $userId,
1293
+        string $app,
1294
+        string $key,
1295
+        string $value,
1296
+        bool $lazy,
1297
+        bool $sensitive,
1298
+        bool $result,
1299
+        ?string $exception = null,
1300
+    ): void {
1301
+        $userConfig = $this->generateUserConfig($preload ?? []);
1302
+        if ($exception !== null) {
1303
+            $this->expectException($exception);
1304
+        }
1305
+
1306
+        $edited = $userConfig->setValueString($userId, $app, $key, $value, $lazy, ($sensitive) ? 1 : 0);
1307
+        if ($exception !== null) {
1308
+            return;
1309
+        }
1310
+
1311
+        $this->assertEquals($result, $edited);
1312
+        if ($result) {
1313
+            $this->assertEquals($value, $userConfig->getValueString($userId, $app, $key, $value, $lazy));
1314
+            $userConfig = $this->generateUserConfig($preload ?? []);
1315
+            $this->assertEquals($value, $userConfig->getValueString($userId, $app, $key, $value, $lazy));
1316
+        }
1317
+    }
1318
+
1319
+    public static function providerSetValueInt(): array {
1320
+        return [
1321
+            [null, 'user1', 'app1', 'key1', 12345, false, false, true],
1322
+            [null, 'user1', 'app1', 'key1', 12345, true, false, true],
1323
+            [null, 'user1', 'app1', 'key1', 12345, true, true, true],
1324
+            [null, 'user1', 'app1', 'fast_int', 11, false, false, false],
1325
+            [null, 'user1', 'app1', 'fast_int', 111, false, false, true],
1326
+            [null, 'user1', 'app1', 'fast_int', 111, true, false, true],
1327
+            [null, 'user1', 'app1', 'fast_int', 111, false, true, true],
1328
+            [null, 'user1', 'app1', 'fast_int', 11, true, false, true],
1329
+            [null, 'user1', 'app1', 'fast_int', 11, false, true, true],
1330
+            [null, 'user1', 'app1', 'lazy_int', 12, false, false, true],
1331
+            [null, 'user1', 'app1', 'lazy_int', 121, false, false, true],
1332
+            [null, 'user1', 'app1', 'lazy_int', 121, true, false, true],
1333
+            [null, 'user1', 'app1', 'lazy_int', 121, false, true, true],
1334
+            [null, 'user1', 'app1', 'lazy_int', 12, true, false, false],
1335
+            [null, 'user1', 'app1', 'lazy_int', 12, false, true, true],
1336
+            [null, 'user1', 'app1', 'fast_string', 12345, false, false, true, TypeConflictException::class],
1337
+            [null, 'user1', 'app1', 'fast_string', 12345, false, false, false, TypeConflictException::class],
1338
+            [null, 'user1', 'app1', 'fast_string', 12345, true, false, true, TypeConflictException::class],
1339
+            [null, 'user1', 'app1', 'fast_string', 12345, true, true, true, TypeConflictException::class],
1340
+            [null, 'user1', 'app1', 'lazy_string', 12345, false, false, true, TypeConflictException::class],
1341
+            [null, 'user1', 'app1', 'lazy_string', 12345, true, false, false, TypeConflictException::class],
1342
+            [null, 'user1', 'app1', 'lazy_string', 12345, true, true, true, TypeConflictException::class],
1343
+            [null, 'user1', 'app1', 'fast_float', 12345, false, false, true, TypeConflictException::class],
1344
+            [null, 'user1', 'app1', 'fast_float', 12345, false, false, true, TypeConflictException::class],
1345
+            [null, 'user1', 'app1', 'lazy_boolean', 12345, false, false, true, TypeConflictException::class],
1346
+            [null, 'user1', 'app1', 'lazy_float', 12345, false, false, true, TypeConflictException::class],
1347
+        ];
1348
+    }
1349
+
1350
+    /**
1351
+     * @dataProvider providerSetValueInt
1352
+     */
1353
+    public function testSetValueInt(
1354
+        ?array $preload,
1355
+        string $userId,
1356
+        string $app,
1357
+        string $key,
1358
+        int $value,
1359
+        bool $lazy,
1360
+        bool $sensitive,
1361
+        bool $result,
1362
+        ?string $exception = null,
1363
+    ): void {
1364
+        $userConfig = $this->generateUserConfig($preload ?? []);
1365
+        if ($exception !== null) {
1366
+            $this->expectException($exception);
1367
+        }
1368
+
1369
+        $edited = $userConfig->setValueInt($userId, $app, $key, $value, $lazy, ($sensitive) ? 1 : 0);
1370
+
1371
+        if ($exception !== null) {
1372
+            return;
1373
+        }
1374
+
1375
+        $this->assertEquals($result, $edited);
1376
+        if ($result) {
1377
+            $this->assertEquals($value, $userConfig->getValueInt($userId, $app, $key, $value, $lazy));
1378
+            $userConfig = $this->generateUserConfig($preload ?? []);
1379
+            $this->assertEquals($value, $userConfig->getValueInt($userId, $app, $key, $value, $lazy));
1380
+        }
1381
+    }
1382
+
1383
+    public static function providerSetValueFloat(): array {
1384
+        return [
1385
+            [null, 'user1', 'app1', 'key1', 12.345, false, false, true],
1386
+            [null, 'user1', 'app1', 'key1', 12.345, true, false, true],
1387
+            [null, 'user1', 'app1', 'key1', 12.345, true, true, true],
1388
+            [null, 'user1', 'app1', 'fast_float', 3.14, false, false, false],
1389
+            [null, 'user1', 'app1', 'fast_float', 3.15, false, false, true],
1390
+            [null, 'user1', 'app1', 'fast_float', 3.15, true, false, true],
1391
+            [null, 'user1', 'app1', 'fast_float', 3.15, false, true, true],
1392
+            [null, 'user1', 'app1', 'fast_float', 3.14, true, false, true],
1393
+            [null, 'user1', 'app1', 'fast_float', 3.14, false, true, true],
1394
+            [null, 'user1', 'app1', 'lazy_float', 3.14159, false, false, true],
1395
+            [null, 'user1', 'app1', 'lazy_float', 3.14158, false, false, true],
1396
+            [null, 'user1', 'app1', 'lazy_float', 3.14158, true, false, true],
1397
+            [null, 'user1', 'app1', 'lazy_float', 3.14158, false, true, true],
1398
+            [null, 'user1', 'app1', 'lazy_float', 3.14159, true, false, false],
1399
+            [null, 'user1', 'app1', 'lazy_float', 3.14159, false, true, true],
1400
+            [null, 'user1', 'app1', 'fast_string', 12.345, false, false, true, TypeConflictException::class],
1401
+            [null, 'user1', 'app1', 'fast_string', 12.345, false, false, false, TypeConflictException::class],
1402
+            [null, 'user1', 'app1', 'fast_string', 12.345, true, false, true, TypeConflictException::class],
1403
+            [null, 'user1', 'app1', 'fast_string', 12.345, true, true, true, TypeConflictException::class],
1404
+            [null, 'user1', 'app1', 'lazy_string', 12.345, false, false, true, TypeConflictException::class],
1405
+            [null, 'user1', 'app1', 'lazy_string', 12.345, true, false, false, TypeConflictException::class],
1406
+            [null, 'user1', 'app1', 'fast_array', 12.345, true, true, true, TypeConflictException::class],
1407
+            [null, 'user1', 'app1', 'fast_int', 12.345, false, false, true, TypeConflictException::class],
1408
+            [null, 'user1', 'app1', 'fast_int', 12.345, false, false, true, TypeConflictException::class],
1409
+            [null, 'user1', 'app1', 'lazy_boolean', 12.345, false, false, true, TypeConflictException::class],
1410
+        ];
1411
+    }
1412
+
1413
+    /**
1414
+     * @dataProvider providerSetValueFloat
1415
+     */
1416
+    public function testSetValueFloat(
1417
+        ?array $preload,
1418
+        string $userId,
1419
+        string $app,
1420
+        string $key,
1421
+        float $value,
1422
+        bool $lazy,
1423
+        bool $sensitive,
1424
+        bool $result,
1425
+        ?string $exception = null,
1426
+    ): void {
1427
+        $userConfig = $this->generateUserConfig($preload ?? []);
1428
+        if ($exception !== null) {
1429
+            $this->expectException($exception);
1430
+        }
1431
+
1432
+        $edited = $userConfig->setValueFloat($userId, $app, $key, $value, $lazy, ($sensitive) ? 1 : 0);
1433
+
1434
+        if ($exception !== null) {
1435
+            return;
1436
+        }
1437
+
1438
+        $this->assertEquals($result, $edited);
1439
+        if ($result) {
1440
+            $this->assertEquals($value, $userConfig->getValueFloat($userId, $app, $key, $value, $lazy));
1441
+            $userConfig = $this->generateUserConfig($preload ?? []);
1442
+            $this->assertEquals($value, $userConfig->getValueFloat($userId, $app, $key, $value, $lazy));
1443
+        }
1444
+    }
1445
+
1446
+
1447
+    public static function providerSetValueArray(): array {
1448
+        return [
1449
+            [null, 'user1', 'app1', 'key1', [], false, false, true],
1450
+            [null, 'user1', 'app1', 'key1', [], true, false, true],
1451
+            [null, 'user1', 'app1', 'key1', [], true, true, true],
1452
+            [null, 'user1', 'app1', 'fast_array', ['year' => 2024], false, false, false],
1453
+            [null, 'user1', 'app1', 'fast_array', [], false, false, true],
1454
+            [null, 'user1', 'app1', 'fast_array', [], true, false, true],
1455
+            [null, 'user1', 'app1', 'fast_array', [], false, true, true],
1456
+            [null, 'user1', 'app1', 'fast_array', ['year' => 2024], true, false, true],
1457
+            [null, 'user1', 'app1', 'fast_array', ['year' => 2024], false, true, true],
1458
+            [null, 'user1', 'app1', 'lazy_array', ['month' => 'October'], false, false, true],
1459
+            [null, 'user1', 'app1', 'lazy_array', ['month' => 'September'], false, false, true],
1460
+            [null, 'user1', 'app1', 'lazy_array', ['month' => 'September'], true, false, true],
1461
+            [null, 'user1', 'app1', 'lazy_array', ['month' => 'September'], false, true, true],
1462
+            [null, 'user1', 'app1', 'lazy_array', ['month' => 'October'], true, false, false],
1463
+            [null, 'user1', 'app1', 'lazy_array', ['month' => 'October'], false, true, true],
1464
+            [null, 'user1', 'app1', 'fast_string', [], false, false, true, TypeConflictException::class],
1465
+            [null, 'user1', 'app1', 'fast_string', [], false, false, false, TypeConflictException::class],
1466
+            [null, 'user1', 'app1', 'fast_string', [], true, false, true, TypeConflictException::class],
1467
+            [null, 'user1', 'app1', 'fast_string', [], true, true, true, TypeConflictException::class],
1468
+            [null, 'user1', 'app1', 'lazy_string', [], false, false, true, TypeConflictException::class],
1469
+            [null, 'user1', 'app1', 'lazy_string', [], true, false, false, TypeConflictException::class],
1470
+            [null, 'user1', 'app1', 'lazy_string', [], true, true, true, TypeConflictException::class],
1471
+            [null, 'user1', 'app1', 'fast_int', [], false, false, true, TypeConflictException::class],
1472
+            [null, 'user1', 'app1', 'fast_int', [], false, false, true, TypeConflictException::class],
1473
+            [null, 'user1', 'app1', 'lazy_boolean', [], false, false, true, TypeConflictException::class],
1474
+        ];
1475
+    }
1476
+
1477
+    /**
1478
+     * @dataProvider providerSetValueArray
1479
+     */
1480
+    public function testSetValueArray(
1481
+        ?array $preload,
1482
+        string $userId,
1483
+        string $app,
1484
+        string $key,
1485
+        array $value,
1486
+        bool $lazy,
1487
+        bool $sensitive,
1488
+        bool $result,
1489
+        ?string $exception = null,
1490
+    ): void {
1491
+        $userConfig = $this->generateUserConfig($preload ?? []);
1492
+        if ($exception !== null) {
1493
+            $this->expectException($exception);
1494
+        }
1495
+
1496
+        $edited = $userConfig->setValueArray($userId, $app, $key, $value, $lazy, ($sensitive) ? 1 : 0);
1497
+
1498
+        if ($exception !== null) {
1499
+            return;
1500
+        }
1501
+
1502
+        $this->assertEquals($result, $edited);
1503
+        if ($result) {
1504
+            $this->assertEqualsCanonicalizing(
1505
+                $value, $userConfig->getValueArray($userId, $app, $key, $value, $lazy)
1506
+            );
1507
+            $userConfig = $this->generateUserConfig($preload ?? []);
1508
+            $this->assertEqualsCanonicalizing(
1509
+                $value, $userConfig->getValueArray($userId, $app, $key, $value, $lazy)
1510
+            );
1511
+        }
1512
+    }
1513
+
1514
+    public static function providerUpdateSensitive(): array {
1515
+        return [
1516
+            [null, 'user1', 'app1', 'key1', false, false],
1517
+            [['user1'], 'user1', 'app1', 'key1', false, false],
1518
+            [null, 'user1', 'app1', 'key1', true, true],
1519
+            [['user1'], 'user1', 'app1', 'key1', true, true],
1520
+        ];
1521
+    }
1522
+
1523
+    /**
1524
+     * @dataProvider providerUpdateSensitive
1525
+     */
1526
+    public function testUpdateSensitive(
1527
+        ?array $preload,
1528
+        string $userId,
1529
+        string $app,
1530
+        string $key,
1531
+        bool $sensitive,
1532
+        bool $result,
1533
+        ?string $exception = null,
1534
+    ): void {
1535
+        $userConfig = $this->generateUserConfig($preload ?? []);
1536
+        if ($exception !== null) {
1537
+            $this->expectException($exception);
1538
+        }
1539
+
1540
+        $edited = $userConfig->updateSensitive($userId, $app, $key, $sensitive);
1541
+        if ($exception !== null) {
1542
+            return;
1543
+        }
1544
+
1545
+        $this->assertEquals($result, $edited);
1546
+        if ($result) {
1547
+            $this->assertEquals($sensitive, $userConfig->isSensitive($userId, $app, $key));
1548
+            $userConfig = $this->generateUserConfig($preload ?? []);
1549
+            $this->assertEquals($sensitive, $userConfig->isSensitive($userId, $app, $key));
1550
+            if ($sensitive) {
1551
+                $this->assertEquals(true, str_starts_with(
1552
+                    $userConfig->statusCache()['fastCache'][$userId][$app][$key] ??
1553
+                    $userConfig->statusCache()['lazyCache'][$userId][$app][$key],
1554
+                    '$UserConfigEncryption$')
1555
+                );
1556
+            }
1557
+        }
1558
+    }
1559
+
1560
+    public static function providerUpdateGlobalSensitive(): array {
1561
+        return [[true], [false]];
1562
+    }
1563
+
1564
+    /**
1565
+     * @dataProvider providerUpdateGlobalSensitive
1566
+     */
1567
+    public function testUpdateGlobalSensitive(bool $sensitive): void {
1568
+        $userConfig = $this->generateUserConfig($preload ?? []);
1569
+        $app = 'app2';
1570
+        if ($sensitive) {
1571
+            $key = 'key2';
1572
+            $value = 'value2a';
1573
+        } else {
1574
+            $key = 'key4';
1575
+            $value = 'value4';
1576
+        }
1577
+
1578
+        $this->assertEquals($value, $userConfig->getValueString('user1', $app, $key));
1579
+        foreach (['user1', 'user2', 'user3', 'user4'] as $userId) {
1580
+            $userConfig->getValueString($userId, $app, $key); // cache loading for userId
1581
+            $this->assertEquals(
1582
+                !$sensitive, str_starts_with(
1583
+                    $userConfig->statusCache()['fastCache'][$userId][$app][$key] ??
1584
+                    $userConfig->statusCache()['lazyCache'][$userId][$app][$key],
1585
+                    '$UserConfigEncryption$'
1586
+                )
1587
+            );
1588
+        }
1589
+
1590
+        $userConfig->updateGlobalSensitive($app, $key, $sensitive);
1591
+
1592
+        $this->assertEquals($value, $userConfig->getValueString('user1', $app, $key));
1593
+        foreach (['user1', 'user2', 'user3', 'user4'] as $userId) {
1594
+            $this->assertEquals($sensitive, $userConfig->isSensitive($userId, $app, $key));
1595
+            // should only work if updateGlobalSensitive drop cache
1596
+            $this->assertEquals($sensitive, str_starts_with(
1597
+                $userConfig->statusCache()['fastCache'][$userId][$app][$key] ??
1598
+                $userConfig->statusCache()['lazyCache'][$userId][$app][$key],
1599
+                '$UserConfigEncryption$')
1600
+            );
1601
+        }
1602
+    }
1603
+
1604
+    public static function providerUpdateLazy(): array {
1605
+        return [
1606
+            [null, 'user1', 'app1', 'key1', false, false],
1607
+            [['user1'], 'user1', 'app1', 'key1', false, false],
1608
+            [null, 'user1', 'app1', 'key1', true, true],
1609
+            [['user1'], 'user1', 'app1', 'key1', true, true],
1610
+        ];
1611
+    }
1612
+
1613
+    /**
1614
+     * @dataProvider providerUpdateLazy
1615
+     */
1616
+    public function testUpdateLazy(
1617
+        ?array $preload,
1618
+        string $userId,
1619
+        string $app,
1620
+        string $key,
1621
+        bool $lazy,
1622
+        bool $result,
1623
+        ?string $exception = null,
1624
+    ): void {
1625
+        $userConfig = $this->generateUserConfig($preload ?? []);
1626
+        if ($exception !== null) {
1627
+            $this->expectException($exception);
1628
+        }
1629
+
1630
+        $edited = $userConfig->updateLazy($userId, $app, $key, $lazy);
1631
+        if ($exception !== null) {
1632
+            return;
1633
+        }
1634
+
1635
+        $this->assertEquals($result, $edited);
1636
+        if ($result) {
1637
+            $this->assertEquals($lazy, $userConfig->isLazy($userId, $app, $key));
1638
+            $userConfig = $this->generateUserConfig($preload ?? []);
1639
+            $this->assertEquals($lazy, $userConfig->isLazy($userId, $app, $key));
1640
+        }
1641
+    }
1642
+
1643
+    public static function providerUpdateGlobalLazy(): array {
1644
+        return [[true], [false]];
1645
+    }
1646
+
1647
+    /**
1648
+     * @dataProvider providerUpdateGlobalLazy
1649
+     */
1650
+    public function testUpdateGlobalLazy(bool $lazy): void {
1651
+        $userConfig = $this->generateUserConfig($preload ?? []);
1652
+        $app = 'app2';
1653
+        if ($lazy) {
1654
+            $key = 'key4';
1655
+            $value = 'value4';
1656
+        } else {
1657
+            $key = 'key3';
1658
+            $value = 'value3';
1659
+        }
1660
+
1661
+        $this->assertEquals($value, $userConfig->getValueString('user1', $app, $key, '', !$lazy));
1662
+        foreach (['user1', 'user2', 'user3', 'user4'] as $userId) {
1663
+            $this->assertEquals(!$lazy, $userConfig->isLazy($userId, $app, $key));
1664
+        }
1665
+
1666
+        $userConfig->updateGlobalLazy($app, $key, $lazy);
1667
+        $this->assertEquals($value, $userConfig->getValueString('user1', $app, $key, '', $lazy));
1668
+        foreach (['user1', 'user2', 'user3', 'user4'] as $userId) {
1669
+            $this->assertEquals($lazy, $userConfig->isLazy($userId, $app, $key));
1670
+        }
1671
+    }
1672
+
1673
+    public static function providerGetDetails(): array {
1674
+        return [
1675
+            [
1676
+                'user3', 'app2', 'key2',
1677
+                [
1678
+                    'userId' => 'user3',
1679
+                    'app' => 'app2',
1680
+                    'key' => 'key2',
1681
+                    'value' => 'value2c',
1682
+                    'type' => 0,
1683
+                    'lazy' => false,
1684
+                    'typeString' => 'mixed',
1685
+                    'sensitive' => false
1686
+                ]
1687
+            ],
1688
+            [
1689
+                'user1', 'app1', 'lazy_int',
1690
+                [
1691
+                    'userId' => 'user1',
1692
+                    'app' => 'app1',
1693
+                    'key' => 'lazy_int',
1694
+                    'value' => 12,
1695
+                    'type' => 2,
1696
+                    'lazy' => true,
1697
+                    'typeString' => 'int',
1698
+                    'sensitive' => false
1699
+                ]
1700
+            ],
1701
+            [
1702
+                'user1', 'app1', 'fast_float_sensitive',
1703
+                [
1704
+                    'userId' => 'user1',
1705
+                    'app' => 'app1',
1706
+                    'key' => 'fast_float_sensitive',
1707
+                    'value' => 1.41,
1708
+                    'type' => 3,
1709
+                    'lazy' => false,
1710
+                    'typeString' => 'float',
1711
+                    'sensitive' => true
1712
+                ]
1713
+            ],
1714
+        ];
1715
+    }
1716
+
1717
+    /**
1718
+     * @dataProvider providerGetDetails
1719
+     */
1720
+    public function testGetDetails(string $userId, string $app, string $key, array $result): void {
1721
+        $userConfig = $this->generateUserConfig($preload ?? []);
1722
+        $this->assertEqualsCanonicalizing($result, $userConfig->getDetails($userId, $app, $key));
1723
+    }
1724
+
1725
+
1726
+    public static function providerDeletePreference(): array {
1727
+        return [
1728
+            [null, 'user1', 'app1', 'key22'],
1729
+            [['user1'], 'user1', 'app1', 'fast_string_sensitive'],
1730
+            [null, 'user1', 'app1', 'lazy_array_sensitive'],
1731
+            [['user2'], 'user1', 'app1', 'lazy_array_sensitive'],
1732
+            [null, 'user2', 'only-lazy', 'key1'],
1733
+            [['user2'], 'user2', 'only-lazy', 'key1'],
1734
+        ];
1735
+    }
1736
+
1737
+    /**
1738
+     * @dataProvider providerDeletePreference
1739
+     */
1740
+    public function testDeletePreference(
1741
+        ?array $preload,
1742
+        string $userId,
1743
+        string $app,
1744
+        string $key,
1745
+    ): void {
1746
+        $userConfig = $this->generateUserConfig($preload ?? []);
1747
+        $lazy = $userConfig->isLazy($userId, $app, $key);
1748
+
1749
+        $userConfig = $this->generateUserConfig($preload ?? []);
1750
+        $this->assertEquals(true, $userConfig->hasKey($userId, $app, $key, $lazy));
1751
+        $userConfig->deleteUserConfig($userId, $app, $key);
1752
+        $this->assertEquals(false, $userConfig->hasKey($userId, $app, $key, $lazy));
1753
+        $userConfig = $this->generateUserConfig($preload ?? []);
1754
+        $this->assertEquals(false, $userConfig->hasKey($userId, $app, $key, $lazy));
1755
+    }
1756
+
1757
+    public static function providerDeleteKey(): array {
1758
+        return [
1759
+            [null, 'app2', 'key3'],
1760
+            [['user1'], 'app2', 'key3'],
1761
+            [null, 'only-lazy', 'key1'],
1762
+            [['user2'], 'only-lazy', 'key1'],
1763
+            [null, 'app2', 'lazy_string_sensitive'],
1764
+            [['user3', 'user1'], 'app2', 'lazy_string_sensitive'],
1765
+        ];
1766
+    }
1767
+
1768
+    /**
1769
+     * @dataProvider providerDeleteKey
1770
+     */
1771
+    public function testDeleteKey(
1772
+        ?array $preload,
1773
+        string $app,
1774
+        string $key,
1775
+    ): void {
1776
+        $userConfig = $this->generateUserConfig($preload ?? []);
1777
+        $userConfig->deleteKey($app, $key);
1778
+
1779
+        foreach (['user1', 'user2', 'user3', 'user4'] as $userId) {
1780
+            $this->assertEquals(false, $userConfig->hasKey($userId, $app, $key, null));
1781
+            $userConfigTemp = $this->generateUserConfig($preload ?? []);
1782
+            $this->assertEquals(false, $userConfigTemp->hasKey($userId, $app, $key, null));
1783
+        }
1784
+    }
1785
+
1786
+    public function testDeleteApp(): void {
1787
+        $userConfig = $this->generateUserConfig();
1788
+        $userConfig->deleteApp('only-lazy');
1789
+
1790
+        foreach (['user1', 'user2', 'user3', 'user4'] as $userId) {
1791
+            $this->assertEquals(false, in_array('only-lazy', $userConfig->getApps($userId)));
1792
+            $userConfigTemp = $this->generateUserConfig();
1793
+            $this->assertEquals(false, in_array('only-lazy', $userConfigTemp->getApps($userId)));
1794
+        }
1795
+    }
1796
+
1797
+    public function testDeleteAllPreferences(): void {
1798
+        $userConfig = $this->generateUserConfig();
1799
+        $userConfig->deleteAllUserConfig('user1');
1800
+
1801
+        $this->assertEqualsCanonicalizing([], $userConfig->getApps('user1'));
1802
+        $userConfig = $this->generateUserConfig();
1803
+        $this->assertEqualsCanonicalizing([], $userConfig->getApps('user1'));
1804
+    }
1805
+
1806
+    public function testClearCache(): void {
1807
+        $userConfig = $this->generateUserConfig(['user1', 'user2']);
1808
+        $userConfig->clearCache('user1');
1809
+
1810
+        $this->assertEquals(true, $userConfig->statusCache()['fastLoaded']['user2']);
1811
+        $this->assertEquals(false, $userConfig->statusCache()['fastLoaded']['user1']);
1812
+        $this->assertEquals('value2a', $userConfig->getValueString('user1', 'app2', 'key2'));
1813
+        $this->assertEquals(false, $userConfig->statusCache()['lazyLoaded']['user1']);
1814
+        $this->assertEquals(true, $userConfig->statusCache()['fastLoaded']['user1']);
1815
+    }
1816
+
1817
+    public function testClearCacheAll(): void {
1818
+        $userConfig = $this->generateUserConfig(['user1', 'user2']);
1819
+        $userConfig->clearCacheAll();
1820
+        $this->assertEqualsCanonicalizing(
1821
+            [
1822
+                'fastLoaded' => [],
1823
+                'fastCache' => [],
1824
+                'lazyLoaded' => [],
1825
+                'lazyCache' => [],
1826
+                'valueDetails' => [],
1827
+            ],
1828
+            $userConfig->statusCache()
1829
+        );
1830
+    }
1831 1831
 }
Please login to merge, or discard this patch.
tests/lib/AppTest.php 1 patch
Indentation   +614 added lines, -614 removed lines patch added patch discarded remove patch
@@ -28,618 +28,618 @@
 block discarded – undo
28 28
  * @group DB
29 29
  */
30 30
 class AppTest extends \Test\TestCase {
31
-	public const TEST_USER1 = 'user1';
32
-	public const TEST_USER2 = 'user2';
33
-	public const TEST_USER3 = 'user3';
34
-	public const TEST_GROUP1 = 'group1';
35
-	public const TEST_GROUP2 = 'group2';
36
-
37
-	public static function appVersionsProvider(): array {
38
-		return [
39
-			// exact match
40
-			[
41
-				'6.0.0.0',
42
-				[
43
-					'requiremin' => '6.0',
44
-					'requiremax' => '6.0',
45
-				],
46
-				true
47
-			],
48
-			// in-between match
49
-			[
50
-				'6.0.0.0',
51
-				[
52
-					'requiremin' => '5.0',
53
-					'requiremax' => '7.0',
54
-				],
55
-				true
56
-			],
57
-			// app too old
58
-			[
59
-				'6.0.0.0',
60
-				[
61
-					'requiremin' => '5.0',
62
-					'requiremax' => '5.0',
63
-				],
64
-				false
65
-			],
66
-			// app too new
67
-			[
68
-				'5.0.0.0',
69
-				[
70
-					'requiremin' => '6.0',
71
-					'requiremax' => '6.0',
72
-				],
73
-				false
74
-			],
75
-			// only min specified
76
-			[
77
-				'6.0.0.0',
78
-				[
79
-					'requiremin' => '6.0',
80
-				],
81
-				true
82
-			],
83
-			// only min specified fail
84
-			[
85
-				'5.0.0.0',
86
-				[
87
-					'requiremin' => '6.0',
88
-				],
89
-				false
90
-			],
91
-			// only min specified legacy
92
-			[
93
-				'6.0.0.0',
94
-				[
95
-					'require' => '6.0',
96
-				],
97
-				true
98
-			],
99
-			// only min specified legacy fail
100
-			[
101
-				'4.0.0.0',
102
-				[
103
-					'require' => '6.0',
104
-				],
105
-				false
106
-			],
107
-			// only max specified
108
-			[
109
-				'5.0.0.0',
110
-				[
111
-					'requiremax' => '6.0',
112
-				],
113
-				true
114
-			],
115
-			// only max specified fail
116
-			[
117
-				'7.0.0.0',
118
-				[
119
-					'requiremax' => '6.0',
120
-				],
121
-				false
122
-			],
123
-			// variations of versions
124
-			// single OC number
125
-			[
126
-				'4',
127
-				[
128
-					'require' => '4.0',
129
-				],
130
-				true
131
-			],
132
-			// multiple OC number
133
-			[
134
-				'4.3.1',
135
-				[
136
-					'require' => '4.3',
137
-				],
138
-				true
139
-			],
140
-			// single app number
141
-			[
142
-				'4',
143
-				[
144
-					'require' => '4',
145
-				],
146
-				true
147
-			],
148
-			// single app number fail
149
-			[
150
-				'4.3',
151
-				[
152
-					'require' => '5',
153
-				],
154
-				false
155
-			],
156
-			// complex
157
-			[
158
-				'5.0.0',
159
-				[
160
-					'require' => '4.5.1',
161
-				],
162
-				true
163
-			],
164
-			// complex fail
165
-			[
166
-				'4.3.1',
167
-				[
168
-					'require' => '4.3.2',
169
-				],
170
-				false
171
-			],
172
-			// two numbers
173
-			[
174
-				'4.3.1',
175
-				[
176
-					'require' => '4.4',
177
-				],
178
-				false
179
-			],
180
-			// one number fail
181
-			[
182
-				'4.3.1',
183
-				[
184
-					'require' => '5',
185
-				],
186
-				false
187
-			],
188
-			// pre-alpha app
189
-			[
190
-				'5.0.3',
191
-				[
192
-					'require' => '4.93',
193
-				],
194
-				true
195
-			],
196
-			// pre-alpha OC
197
-			[
198
-				'6.90.0.2',
199
-				[
200
-					'require' => '6.90',
201
-				],
202
-				true
203
-			],
204
-			// pre-alpha OC max
205
-			[
206
-				'6.90.0.2',
207
-				[
208
-					'requiremax' => '7',
209
-				],
210
-				true
211
-			],
212
-			// expect same major number match
213
-			[
214
-				'5.0.3',
215
-				[
216
-					'require' => '5',
217
-				],
218
-				true
219
-			],
220
-			// expect same major number match
221
-			[
222
-				'5.0.3',
223
-				[
224
-					'requiremax' => '5',
225
-				],
226
-				true
227
-			],
228
-			// dependencies versions before require*
229
-			[
230
-				'6.0.0.0',
231
-				[
232
-					'requiremin' => '5.0',
233
-					'requiremax' => '7.0',
234
-					'dependencies' => [
235
-						'owncloud' => [
236
-							'@attributes' => [
237
-								'min-version' => '7.0',
238
-								'max-version' => '7.0',
239
-							],
240
-						],
241
-					],
242
-				],
243
-				false
244
-			],
245
-			[
246
-				'6.0.0.0',
247
-				[
248
-					'requiremin' => '5.0',
249
-					'requiremax' => '7.0',
250
-					'dependencies' => [
251
-						'owncloud' => [
252
-							'@attributes' => [
253
-								'min-version' => '5.0',
254
-								'max-version' => '5.0',
255
-							],
256
-						],
257
-					],
258
-				],
259
-				false
260
-			],
261
-			[
262
-				'6.0.0.0',
263
-				[
264
-					'requiremin' => '5.0',
265
-					'requiremax' => '5.0',
266
-					'dependencies' => [
267
-						'owncloud' => [
268
-							'@attributes' => [
269
-								'min-version' => '5.0',
270
-								'max-version' => '7.0',
271
-							],
272
-						],
273
-					],
274
-				],
275
-				true
276
-			],
277
-			[
278
-				'9.2.0.0',
279
-				[
280
-					'dependencies' => [
281
-						'owncloud' => [
282
-							'@attributes' => [
283
-								'min-version' => '9.0',
284
-								'max-version' => '9.1',
285
-							],
286
-						],
287
-						'nextcloud' => [
288
-							'@attributes' => [
289
-								'min-version' => '9.1',
290
-								'max-version' => '9.2',
291
-							],
292
-						],
293
-					],
294
-				],
295
-				true
296
-			],
297
-			[
298
-				'9.2.0.0',
299
-				[
300
-					'dependencies' => [
301
-						'nextcloud' => [
302
-							'@attributes' => [
303
-								'min-version' => '9.1',
304
-								'max-version' => '9.2',
305
-							],
306
-						],
307
-					],
308
-				],
309
-				true
310
-			],
311
-		];
312
-	}
313
-
314
-	/**
315
-	 * @dataProvider appVersionsProvider
316
-	 */
317
-	public function testIsAppCompatible($ocVersion, $appInfo, $expectedResult): void {
318
-		$this->assertEquals($expectedResult, \OC_App::isAppCompatible($ocVersion, $appInfo));
319
-	}
320
-
321
-	/**
322
-	 * Tests that the app order is correct
323
-	 */
324
-	public function testGetEnabledAppsIsSorted(): void {
325
-		$apps = \OC_App::getEnabledApps();
326
-		// copy array
327
-		$sortedApps = $apps;
328
-		sort($sortedApps);
329
-		// 'files' is always on top
330
-		unset($sortedApps[array_search('files', $sortedApps)]);
331
-		array_unshift($sortedApps, 'files');
332
-		$this->assertEquals($sortedApps, $apps);
333
-	}
334
-
335
-	/**
336
-	 * Providers for the app config values
337
-	 */
338
-	public static function appConfigValuesProvider(): array {
339
-		return [
340
-			// logged in user1
341
-			[
342
-				self::TEST_USER1,
343
-				[
344
-					'files',
345
-					'app1',
346
-					'app3',
347
-					'appforgroup1',
348
-					'appforgroup12',
349
-					'cloud_federation_api',
350
-					'dav',
351
-					'federatedfilesharing',
352
-					'lookup_server_connector',
353
-					'oauth2',
354
-					'profile',
355
-					'provisioning_api',
356
-					'settings',
357
-					'theming',
358
-					'twofactor_backupcodes',
359
-					'viewer',
360
-					'workflowengine',
361
-				],
362
-				false
363
-			],
364
-			// logged in user2
365
-			[
366
-				self::TEST_USER2,
367
-				[
368
-					'files',
369
-					'app1',
370
-					'app3',
371
-					'appforgroup12',
372
-					'appforgroup2',
373
-					'cloud_federation_api',
374
-					'dav',
375
-					'federatedfilesharing',
376
-					'lookup_server_connector',
377
-					'oauth2',
378
-					'profile',
379
-					'provisioning_api',
380
-					'settings',
381
-					'theming',
382
-					'twofactor_backupcodes',
383
-					'viewer',
384
-					'workflowengine',
385
-				],
386
-				false
387
-			],
388
-			// logged in user3
389
-			[
390
-				self::TEST_USER3,
391
-				[
392
-					'files',
393
-					'app1',
394
-					'app3',
395
-					'appforgroup1',
396
-					'appforgroup12',
397
-					'appforgroup2',
398
-					'cloud_federation_api',
399
-					'dav',
400
-					'federatedfilesharing',
401
-					'lookup_server_connector',
402
-					'oauth2',
403
-					'profile',
404
-					'provisioning_api',
405
-					'settings',
406
-					'theming',
407
-					'twofactor_backupcodes',
408
-					'viewer',
409
-					'workflowengine',
410
-				],
411
-				false
412
-			],
413
-			//  no user, returns all apps
414
-			[
415
-				null,
416
-				[
417
-					'files',
418
-					'app1',
419
-					'app3',
420
-					'appforgroup1',
421
-					'appforgroup12',
422
-					'appforgroup2',
423
-					'cloud_federation_api',
424
-					'dav',
425
-					'federatedfilesharing',
426
-					'lookup_server_connector',
427
-					'oauth2',
428
-					'profile',
429
-					'provisioning_api',
430
-					'settings',
431
-					'theming',
432
-					'twofactor_backupcodes',
433
-					'viewer',
434
-					'workflowengine',
435
-				],
436
-				false,
437
-			],
438
-			//  user given, but ask for all
439
-			[
440
-				self::TEST_USER1,
441
-				[
442
-					'files',
443
-					'app1',
444
-					'app3',
445
-					'appforgroup1',
446
-					'appforgroup12',
447
-					'appforgroup2',
448
-					'cloud_federation_api',
449
-					'dav',
450
-					'federatedfilesharing',
451
-					'lookup_server_connector',
452
-					'oauth2',
453
-					'profile',
454
-					'provisioning_api',
455
-					'settings',
456
-					'theming',
457
-					'twofactor_backupcodes',
458
-					'viewer',
459
-					'workflowengine',
460
-				],
461
-				true,
462
-			],
463
-		];
464
-	}
465
-
466
-	/**
467
-	 * Test enabled apps
468
-	 *
469
-	 * @dataProvider appConfigValuesProvider
470
-	 */
471
-	public function testEnabledApps($user, $expectedApps, $forceAll): void {
472
-		$userManager = \OCP\Server::get(IUserManager::class);
473
-		$groupManager = \OCP\Server::get(IGroupManager::class);
474
-		$user1 = $userManager->createUser(self::TEST_USER1, 'NotAnEasyPassword123456+');
475
-		$user2 = $userManager->createUser(self::TEST_USER2, 'NotAnEasyPassword123456_');
476
-		$user3 = $userManager->createUser(self::TEST_USER3, 'NotAnEasyPassword123456?');
477
-
478
-		$group1 = $groupManager->createGroup(self::TEST_GROUP1);
479
-		$group1->addUser($user1);
480
-		$group1->addUser($user3);
481
-		$group2 = $groupManager->createGroup(self::TEST_GROUP2);
482
-		$group2->addUser($user2);
483
-		$group2->addUser($user3);
484
-
485
-		\OC_User::setUserId($user);
486
-
487
-		$this->setupAppConfigMock()->expects($this->once())
488
-			->method('getValues')
489
-			->willReturn(
490
-				[
491
-					'app3' => 'yes',
492
-					'app2' => 'no',
493
-					'app1' => 'yes',
494
-					'appforgroup1' => '["group1"]',
495
-					'appforgroup2' => '["group2"]',
496
-					'appforgroup12' => '["group2","group1"]',
497
-				]
498
-
499
-			);
500
-
501
-		$apps = \OC_App::getEnabledApps(false, $forceAll);
502
-
503
-		$this->restoreAppConfig();
504
-		\OC_User::setUserId(null);
505
-
506
-		$user1->delete();
507
-		$user2->delete();
508
-		$user3->delete();
509
-
510
-		$group1->delete();
511
-		$group2->delete();
512
-
513
-		$this->assertEquals($expectedApps, $apps);
514
-	}
515
-
516
-	/**
517
-	 * Test isEnabledApps() with cache, not re-reading the list of
518
-	 * enabled apps more than once when a user is set.
519
-	 */
520
-	public function testEnabledAppsCache(): void {
521
-		$userManager = \OCP\Server::get(IUserManager::class);
522
-		$user1 = $userManager->createUser(self::TEST_USER1, 'NotAnEasyPassword123456+');
523
-
524
-		\OC_User::setUserId(self::TEST_USER1);
525
-
526
-		$this->setupAppConfigMock()->expects($this->once())
527
-			->method('getValues')
528
-			->willReturn(
529
-				[
530
-					'app3' => 'yes',
531
-					'app2' => 'no',
532
-				]
533
-
534
-			);
535
-
536
-		$apps = \OC_App::getEnabledApps();
537
-		$this->assertEquals(['files', 'app3', 'cloud_federation_api', 'dav', 'federatedfilesharing', 'lookup_server_connector', 'oauth2', 'profile', 'provisioning_api', 'settings', 'theming', 'twofactor_backupcodes', 'viewer', 'workflowengine'], $apps);
538
-
539
-		// mock should not be called again here
540
-		$apps = \OC_App::getEnabledApps();
541
-		$this->assertEquals(['files', 'app3', 'cloud_federation_api', 'dav', 'federatedfilesharing', 'lookup_server_connector', 'oauth2', 'profile', 'provisioning_api', 'settings', 'theming', 'twofactor_backupcodes', 'viewer', 'workflowengine'], $apps);
542
-
543
-		$this->restoreAppConfig();
544
-		\OC_User::setUserId(null);
545
-
546
-		$user1->delete();
547
-	}
548
-
549
-
550
-	private function setupAppConfigMock() {
551
-		/** @var AppConfig|MockObject */
552
-		$appConfig = $this->getMockBuilder(AppConfig::class)
553
-			->onlyMethods(['getValues'])
554
-			->setConstructorArgs([\OCP\Server::get(IDBConnection::class)])
555
-			->disableOriginalConstructor()
556
-			->getMock();
557
-
558
-		$this->registerAppConfig($appConfig);
559
-		return $appConfig;
560
-	}
561
-
562
-	/**
563
-	 * Register an app config mock for testing purposes.
564
-	 *
565
-	 * @param IAppConfig $appConfig app config mock
566
-	 */
567
-	private function registerAppConfig(AppConfig $appConfig) {
568
-		$this->overwriteService(AppConfig::class, $appConfig);
569
-		$this->overwriteService(AppManager::class, new AppManager(
570
-			\OCP\Server::get(IUserSession::class),
571
-			\OCP\Server::get(IConfig::class),
572
-			\OCP\Server::get(IGroupManager::class),
573
-			\OCP\Server::get(ICacheFactory::class),
574
-			\OCP\Server::get(IEventDispatcher::class),
575
-			\OCP\Server::get(LoggerInterface::class),
576
-			\OCP\Server::get(ServerVersion::class),
577
-		));
578
-	}
579
-
580
-	/**
581
-	 * Restore the original app config service.
582
-	 */
583
-	private function restoreAppConfig() {
584
-		$this->restoreService(AppConfig::class);
585
-		$this->restoreService(AppManager::class);
586
-
587
-		// Remove the cache of the mocked apps list with a forceRefresh
588
-		\OC_App::getEnabledApps();
589
-	}
590
-
591
-	/**
592
-	 * Providers for the app data values
593
-	 */
594
-	public static function appDataProvider(): array {
595
-		return [
596
-			[
597
-				['description' => " \t  This is a multiline \n test with \n \t \n \n some new lines   "],
598
-				['description' => "This is a multiline \n test with \n \t \n \n some new lines"],
599
-			],
600
-			[
601
-				['description' => " \t  This is a multiline \n test with \n \t   some new lines   "],
602
-				['description' => "This is a multiline \n test with \n \t   some new lines"],
603
-			],
604
-			[
605
-				['description' => hex2bin('5065726d657420646520732761757468656e7469666965722064616e732070697769676f20646972656374656d656e74206176656320736573206964656e74696669616e7473206f776e636c6f75642073616e73206c65732072657461706572206574206d657420c3a0206a6f757273206365757820636920656e20636173206465206368616e67656d656e74206465206d6f742064652070617373652e0d0a0d')],
606
-				['description' => "Permet de s'authentifier dans piwigo directement avec ses identifiants owncloud sans les retaper et met à jours ceux ci en cas de changement de mot de passe."],
607
-			],
608
-			[
609
-				['not-a-description' => " \t  This is a multiline \n test with \n \t   some new lines   "],
610
-				[
611
-					'not-a-description' => " \t  This is a multiline \n test with \n \t   some new lines   ",
612
-					'description' => '',
613
-				],
614
-			],
615
-			[
616
-				['description' => [100, 'bla']],
617
-				['description' => ''],
618
-			],
619
-		];
620
-	}
621
-
622
-	/**
623
-	 * Test app info parser
624
-	 *
625
-	 * @dataProvider appDataProvider
626
-	 * @param array $data
627
-	 * @param array $expected
628
-	 */
629
-	public function testParseAppInfo(array $data, array $expected): void {
630
-		$this->assertSame($expected, \OC_App::parseAppInfo($data));
631
-	}
632
-
633
-	public function testParseAppInfoL10N(): void {
634
-		$parser = new InfoParser();
635
-		$data = $parser->parse(\OC::$SERVERROOT . '/tests/data/app/description-multi-lang.xml');
636
-		$this->assertEquals('English', \OC_App::parseAppInfo($data, 'en')['description']);
637
-		$this->assertEquals('German', \OC_App::parseAppInfo($data, 'de')['description']);
638
-	}
639
-
640
-	public function testParseAppInfoL10NSingleLanguage(): void {
641
-		$parser = new InfoParser();
642
-		$data = $parser->parse(\OC::$SERVERROOT . '/tests/data/app/description-single-lang.xml');
643
-		$this->assertEquals('English', \OC_App::parseAppInfo($data, 'en')['description']);
644
-	}
31
+    public const TEST_USER1 = 'user1';
32
+    public const TEST_USER2 = 'user2';
33
+    public const TEST_USER3 = 'user3';
34
+    public const TEST_GROUP1 = 'group1';
35
+    public const TEST_GROUP2 = 'group2';
36
+
37
+    public static function appVersionsProvider(): array {
38
+        return [
39
+            // exact match
40
+            [
41
+                '6.0.0.0',
42
+                [
43
+                    'requiremin' => '6.0',
44
+                    'requiremax' => '6.0',
45
+                ],
46
+                true
47
+            ],
48
+            // in-between match
49
+            [
50
+                '6.0.0.0',
51
+                [
52
+                    'requiremin' => '5.0',
53
+                    'requiremax' => '7.0',
54
+                ],
55
+                true
56
+            ],
57
+            // app too old
58
+            [
59
+                '6.0.0.0',
60
+                [
61
+                    'requiremin' => '5.0',
62
+                    'requiremax' => '5.0',
63
+                ],
64
+                false
65
+            ],
66
+            // app too new
67
+            [
68
+                '5.0.0.0',
69
+                [
70
+                    'requiremin' => '6.0',
71
+                    'requiremax' => '6.0',
72
+                ],
73
+                false
74
+            ],
75
+            // only min specified
76
+            [
77
+                '6.0.0.0',
78
+                [
79
+                    'requiremin' => '6.0',
80
+                ],
81
+                true
82
+            ],
83
+            // only min specified fail
84
+            [
85
+                '5.0.0.0',
86
+                [
87
+                    'requiremin' => '6.0',
88
+                ],
89
+                false
90
+            ],
91
+            // only min specified legacy
92
+            [
93
+                '6.0.0.0',
94
+                [
95
+                    'require' => '6.0',
96
+                ],
97
+                true
98
+            ],
99
+            // only min specified legacy fail
100
+            [
101
+                '4.0.0.0',
102
+                [
103
+                    'require' => '6.0',
104
+                ],
105
+                false
106
+            ],
107
+            // only max specified
108
+            [
109
+                '5.0.0.0',
110
+                [
111
+                    'requiremax' => '6.0',
112
+                ],
113
+                true
114
+            ],
115
+            // only max specified fail
116
+            [
117
+                '7.0.0.0',
118
+                [
119
+                    'requiremax' => '6.0',
120
+                ],
121
+                false
122
+            ],
123
+            // variations of versions
124
+            // single OC number
125
+            [
126
+                '4',
127
+                [
128
+                    'require' => '4.0',
129
+                ],
130
+                true
131
+            ],
132
+            // multiple OC number
133
+            [
134
+                '4.3.1',
135
+                [
136
+                    'require' => '4.3',
137
+                ],
138
+                true
139
+            ],
140
+            // single app number
141
+            [
142
+                '4',
143
+                [
144
+                    'require' => '4',
145
+                ],
146
+                true
147
+            ],
148
+            // single app number fail
149
+            [
150
+                '4.3',
151
+                [
152
+                    'require' => '5',
153
+                ],
154
+                false
155
+            ],
156
+            // complex
157
+            [
158
+                '5.0.0',
159
+                [
160
+                    'require' => '4.5.1',
161
+                ],
162
+                true
163
+            ],
164
+            // complex fail
165
+            [
166
+                '4.3.1',
167
+                [
168
+                    'require' => '4.3.2',
169
+                ],
170
+                false
171
+            ],
172
+            // two numbers
173
+            [
174
+                '4.3.1',
175
+                [
176
+                    'require' => '4.4',
177
+                ],
178
+                false
179
+            ],
180
+            // one number fail
181
+            [
182
+                '4.3.1',
183
+                [
184
+                    'require' => '5',
185
+                ],
186
+                false
187
+            ],
188
+            // pre-alpha app
189
+            [
190
+                '5.0.3',
191
+                [
192
+                    'require' => '4.93',
193
+                ],
194
+                true
195
+            ],
196
+            // pre-alpha OC
197
+            [
198
+                '6.90.0.2',
199
+                [
200
+                    'require' => '6.90',
201
+                ],
202
+                true
203
+            ],
204
+            // pre-alpha OC max
205
+            [
206
+                '6.90.0.2',
207
+                [
208
+                    'requiremax' => '7',
209
+                ],
210
+                true
211
+            ],
212
+            // expect same major number match
213
+            [
214
+                '5.0.3',
215
+                [
216
+                    'require' => '5',
217
+                ],
218
+                true
219
+            ],
220
+            // expect same major number match
221
+            [
222
+                '5.0.3',
223
+                [
224
+                    'requiremax' => '5',
225
+                ],
226
+                true
227
+            ],
228
+            // dependencies versions before require*
229
+            [
230
+                '6.0.0.0',
231
+                [
232
+                    'requiremin' => '5.0',
233
+                    'requiremax' => '7.0',
234
+                    'dependencies' => [
235
+                        'owncloud' => [
236
+                            '@attributes' => [
237
+                                'min-version' => '7.0',
238
+                                'max-version' => '7.0',
239
+                            ],
240
+                        ],
241
+                    ],
242
+                ],
243
+                false
244
+            ],
245
+            [
246
+                '6.0.0.0',
247
+                [
248
+                    'requiremin' => '5.0',
249
+                    'requiremax' => '7.0',
250
+                    'dependencies' => [
251
+                        'owncloud' => [
252
+                            '@attributes' => [
253
+                                'min-version' => '5.0',
254
+                                'max-version' => '5.0',
255
+                            ],
256
+                        ],
257
+                    ],
258
+                ],
259
+                false
260
+            ],
261
+            [
262
+                '6.0.0.0',
263
+                [
264
+                    'requiremin' => '5.0',
265
+                    'requiremax' => '5.0',
266
+                    'dependencies' => [
267
+                        'owncloud' => [
268
+                            '@attributes' => [
269
+                                'min-version' => '5.0',
270
+                                'max-version' => '7.0',
271
+                            ],
272
+                        ],
273
+                    ],
274
+                ],
275
+                true
276
+            ],
277
+            [
278
+                '9.2.0.0',
279
+                [
280
+                    'dependencies' => [
281
+                        'owncloud' => [
282
+                            '@attributes' => [
283
+                                'min-version' => '9.0',
284
+                                'max-version' => '9.1',
285
+                            ],
286
+                        ],
287
+                        'nextcloud' => [
288
+                            '@attributes' => [
289
+                                'min-version' => '9.1',
290
+                                'max-version' => '9.2',
291
+                            ],
292
+                        ],
293
+                    ],
294
+                ],
295
+                true
296
+            ],
297
+            [
298
+                '9.2.0.0',
299
+                [
300
+                    'dependencies' => [
301
+                        'nextcloud' => [
302
+                            '@attributes' => [
303
+                                'min-version' => '9.1',
304
+                                'max-version' => '9.2',
305
+                            ],
306
+                        ],
307
+                    ],
308
+                ],
309
+                true
310
+            ],
311
+        ];
312
+    }
313
+
314
+    /**
315
+     * @dataProvider appVersionsProvider
316
+     */
317
+    public function testIsAppCompatible($ocVersion, $appInfo, $expectedResult): void {
318
+        $this->assertEquals($expectedResult, \OC_App::isAppCompatible($ocVersion, $appInfo));
319
+    }
320
+
321
+    /**
322
+     * Tests that the app order is correct
323
+     */
324
+    public function testGetEnabledAppsIsSorted(): void {
325
+        $apps = \OC_App::getEnabledApps();
326
+        // copy array
327
+        $sortedApps = $apps;
328
+        sort($sortedApps);
329
+        // 'files' is always on top
330
+        unset($sortedApps[array_search('files', $sortedApps)]);
331
+        array_unshift($sortedApps, 'files');
332
+        $this->assertEquals($sortedApps, $apps);
333
+    }
334
+
335
+    /**
336
+     * Providers for the app config values
337
+     */
338
+    public static function appConfigValuesProvider(): array {
339
+        return [
340
+            // logged in user1
341
+            [
342
+                self::TEST_USER1,
343
+                [
344
+                    'files',
345
+                    'app1',
346
+                    'app3',
347
+                    'appforgroup1',
348
+                    'appforgroup12',
349
+                    'cloud_federation_api',
350
+                    'dav',
351
+                    'federatedfilesharing',
352
+                    'lookup_server_connector',
353
+                    'oauth2',
354
+                    'profile',
355
+                    'provisioning_api',
356
+                    'settings',
357
+                    'theming',
358
+                    'twofactor_backupcodes',
359
+                    'viewer',
360
+                    'workflowengine',
361
+                ],
362
+                false
363
+            ],
364
+            // logged in user2
365
+            [
366
+                self::TEST_USER2,
367
+                [
368
+                    'files',
369
+                    'app1',
370
+                    'app3',
371
+                    'appforgroup12',
372
+                    'appforgroup2',
373
+                    'cloud_federation_api',
374
+                    'dav',
375
+                    'federatedfilesharing',
376
+                    'lookup_server_connector',
377
+                    'oauth2',
378
+                    'profile',
379
+                    'provisioning_api',
380
+                    'settings',
381
+                    'theming',
382
+                    'twofactor_backupcodes',
383
+                    'viewer',
384
+                    'workflowengine',
385
+                ],
386
+                false
387
+            ],
388
+            // logged in user3
389
+            [
390
+                self::TEST_USER3,
391
+                [
392
+                    'files',
393
+                    'app1',
394
+                    'app3',
395
+                    'appforgroup1',
396
+                    'appforgroup12',
397
+                    'appforgroup2',
398
+                    'cloud_federation_api',
399
+                    'dav',
400
+                    'federatedfilesharing',
401
+                    'lookup_server_connector',
402
+                    'oauth2',
403
+                    'profile',
404
+                    'provisioning_api',
405
+                    'settings',
406
+                    'theming',
407
+                    'twofactor_backupcodes',
408
+                    'viewer',
409
+                    'workflowengine',
410
+                ],
411
+                false
412
+            ],
413
+            //  no user, returns all apps
414
+            [
415
+                null,
416
+                [
417
+                    'files',
418
+                    'app1',
419
+                    'app3',
420
+                    'appforgroup1',
421
+                    'appforgroup12',
422
+                    'appforgroup2',
423
+                    'cloud_federation_api',
424
+                    'dav',
425
+                    'federatedfilesharing',
426
+                    'lookup_server_connector',
427
+                    'oauth2',
428
+                    'profile',
429
+                    'provisioning_api',
430
+                    'settings',
431
+                    'theming',
432
+                    'twofactor_backupcodes',
433
+                    'viewer',
434
+                    'workflowengine',
435
+                ],
436
+                false,
437
+            ],
438
+            //  user given, but ask for all
439
+            [
440
+                self::TEST_USER1,
441
+                [
442
+                    'files',
443
+                    'app1',
444
+                    'app3',
445
+                    'appforgroup1',
446
+                    'appforgroup12',
447
+                    'appforgroup2',
448
+                    'cloud_federation_api',
449
+                    'dav',
450
+                    'federatedfilesharing',
451
+                    'lookup_server_connector',
452
+                    'oauth2',
453
+                    'profile',
454
+                    'provisioning_api',
455
+                    'settings',
456
+                    'theming',
457
+                    'twofactor_backupcodes',
458
+                    'viewer',
459
+                    'workflowengine',
460
+                ],
461
+                true,
462
+            ],
463
+        ];
464
+    }
465
+
466
+    /**
467
+     * Test enabled apps
468
+     *
469
+     * @dataProvider appConfigValuesProvider
470
+     */
471
+    public function testEnabledApps($user, $expectedApps, $forceAll): void {
472
+        $userManager = \OCP\Server::get(IUserManager::class);
473
+        $groupManager = \OCP\Server::get(IGroupManager::class);
474
+        $user1 = $userManager->createUser(self::TEST_USER1, 'NotAnEasyPassword123456+');
475
+        $user2 = $userManager->createUser(self::TEST_USER2, 'NotAnEasyPassword123456_');
476
+        $user3 = $userManager->createUser(self::TEST_USER3, 'NotAnEasyPassword123456?');
477
+
478
+        $group1 = $groupManager->createGroup(self::TEST_GROUP1);
479
+        $group1->addUser($user1);
480
+        $group1->addUser($user3);
481
+        $group2 = $groupManager->createGroup(self::TEST_GROUP2);
482
+        $group2->addUser($user2);
483
+        $group2->addUser($user3);
484
+
485
+        \OC_User::setUserId($user);
486
+
487
+        $this->setupAppConfigMock()->expects($this->once())
488
+            ->method('getValues')
489
+            ->willReturn(
490
+                [
491
+                    'app3' => 'yes',
492
+                    'app2' => 'no',
493
+                    'app1' => 'yes',
494
+                    'appforgroup1' => '["group1"]',
495
+                    'appforgroup2' => '["group2"]',
496
+                    'appforgroup12' => '["group2","group1"]',
497
+                ]
498
+
499
+            );
500
+
501
+        $apps = \OC_App::getEnabledApps(false, $forceAll);
502
+
503
+        $this->restoreAppConfig();
504
+        \OC_User::setUserId(null);
505
+
506
+        $user1->delete();
507
+        $user2->delete();
508
+        $user3->delete();
509
+
510
+        $group1->delete();
511
+        $group2->delete();
512
+
513
+        $this->assertEquals($expectedApps, $apps);
514
+    }
515
+
516
+    /**
517
+     * Test isEnabledApps() with cache, not re-reading the list of
518
+     * enabled apps more than once when a user is set.
519
+     */
520
+    public function testEnabledAppsCache(): void {
521
+        $userManager = \OCP\Server::get(IUserManager::class);
522
+        $user1 = $userManager->createUser(self::TEST_USER1, 'NotAnEasyPassword123456+');
523
+
524
+        \OC_User::setUserId(self::TEST_USER1);
525
+
526
+        $this->setupAppConfigMock()->expects($this->once())
527
+            ->method('getValues')
528
+            ->willReturn(
529
+                [
530
+                    'app3' => 'yes',
531
+                    'app2' => 'no',
532
+                ]
533
+
534
+            );
535
+
536
+        $apps = \OC_App::getEnabledApps();
537
+        $this->assertEquals(['files', 'app3', 'cloud_federation_api', 'dav', 'federatedfilesharing', 'lookup_server_connector', 'oauth2', 'profile', 'provisioning_api', 'settings', 'theming', 'twofactor_backupcodes', 'viewer', 'workflowengine'], $apps);
538
+
539
+        // mock should not be called again here
540
+        $apps = \OC_App::getEnabledApps();
541
+        $this->assertEquals(['files', 'app3', 'cloud_federation_api', 'dav', 'federatedfilesharing', 'lookup_server_connector', 'oauth2', 'profile', 'provisioning_api', 'settings', 'theming', 'twofactor_backupcodes', 'viewer', 'workflowengine'], $apps);
542
+
543
+        $this->restoreAppConfig();
544
+        \OC_User::setUserId(null);
545
+
546
+        $user1->delete();
547
+    }
548
+
549
+
550
+    private function setupAppConfigMock() {
551
+        /** @var AppConfig|MockObject */
552
+        $appConfig = $this->getMockBuilder(AppConfig::class)
553
+            ->onlyMethods(['getValues'])
554
+            ->setConstructorArgs([\OCP\Server::get(IDBConnection::class)])
555
+            ->disableOriginalConstructor()
556
+            ->getMock();
557
+
558
+        $this->registerAppConfig($appConfig);
559
+        return $appConfig;
560
+    }
561
+
562
+    /**
563
+     * Register an app config mock for testing purposes.
564
+     *
565
+     * @param IAppConfig $appConfig app config mock
566
+     */
567
+    private function registerAppConfig(AppConfig $appConfig) {
568
+        $this->overwriteService(AppConfig::class, $appConfig);
569
+        $this->overwriteService(AppManager::class, new AppManager(
570
+            \OCP\Server::get(IUserSession::class),
571
+            \OCP\Server::get(IConfig::class),
572
+            \OCP\Server::get(IGroupManager::class),
573
+            \OCP\Server::get(ICacheFactory::class),
574
+            \OCP\Server::get(IEventDispatcher::class),
575
+            \OCP\Server::get(LoggerInterface::class),
576
+            \OCP\Server::get(ServerVersion::class),
577
+        ));
578
+    }
579
+
580
+    /**
581
+     * Restore the original app config service.
582
+     */
583
+    private function restoreAppConfig() {
584
+        $this->restoreService(AppConfig::class);
585
+        $this->restoreService(AppManager::class);
586
+
587
+        // Remove the cache of the mocked apps list with a forceRefresh
588
+        \OC_App::getEnabledApps();
589
+    }
590
+
591
+    /**
592
+     * Providers for the app data values
593
+     */
594
+    public static function appDataProvider(): array {
595
+        return [
596
+            [
597
+                ['description' => " \t  This is a multiline \n test with \n \t \n \n some new lines   "],
598
+                ['description' => "This is a multiline \n test with \n \t \n \n some new lines"],
599
+            ],
600
+            [
601
+                ['description' => " \t  This is a multiline \n test with \n \t   some new lines   "],
602
+                ['description' => "This is a multiline \n test with \n \t   some new lines"],
603
+            ],
604
+            [
605
+                ['description' => hex2bin('5065726d657420646520732761757468656e7469666965722064616e732070697769676f20646972656374656d656e74206176656320736573206964656e74696669616e7473206f776e636c6f75642073616e73206c65732072657461706572206574206d657420c3a0206a6f757273206365757820636920656e20636173206465206368616e67656d656e74206465206d6f742064652070617373652e0d0a0d')],
606
+                ['description' => "Permet de s'authentifier dans piwigo directement avec ses identifiants owncloud sans les retaper et met à jours ceux ci en cas de changement de mot de passe."],
607
+            ],
608
+            [
609
+                ['not-a-description' => " \t  This is a multiline \n test with \n \t   some new lines   "],
610
+                [
611
+                    'not-a-description' => " \t  This is a multiline \n test with \n \t   some new lines   ",
612
+                    'description' => '',
613
+                ],
614
+            ],
615
+            [
616
+                ['description' => [100, 'bla']],
617
+                ['description' => ''],
618
+            ],
619
+        ];
620
+    }
621
+
622
+    /**
623
+     * Test app info parser
624
+     *
625
+     * @dataProvider appDataProvider
626
+     * @param array $data
627
+     * @param array $expected
628
+     */
629
+    public function testParseAppInfo(array $data, array $expected): void {
630
+        $this->assertSame($expected, \OC_App::parseAppInfo($data));
631
+    }
632
+
633
+    public function testParseAppInfoL10N(): void {
634
+        $parser = new InfoParser();
635
+        $data = $parser->parse(\OC::$SERVERROOT . '/tests/data/app/description-multi-lang.xml');
636
+        $this->assertEquals('English', \OC_App::parseAppInfo($data, 'en')['description']);
637
+        $this->assertEquals('German', \OC_App::parseAppInfo($data, 'de')['description']);
638
+    }
639
+
640
+    public function testParseAppInfoL10NSingleLanguage(): void {
641
+        $parser = new InfoParser();
642
+        $data = $parser->parse(\OC::$SERVERROOT . '/tests/data/app/description-single-lang.xml');
643
+        $this->assertEquals('English', \OC_App::parseAppInfo($data, 'en')['description']);
644
+    }
645 645
 }
Please login to merge, or discard this patch.
tests/lib/UpdaterTest.php 1 patch
Indentation   +79 added lines, -79 removed lines patch added patch discarded remove patch
@@ -17,89 +17,89 @@
 block discarded – undo
17 17
 use Psr\Log\LoggerInterface;
18 18
 
19 19
 class UpdaterTest extends TestCase {
20
-	/** @var ServerVersion|MockObject */
21
-	private $serverVersion;
22
-	/** @var IConfig|MockObject */
23
-	private $config;
24
-	/** @var IAppConfig|MockObject */
25
-	private $appConfig;
26
-	/** @var LoggerInterface|MockObject */
27
-	private $logger;
28
-	/** @var Updater */
29
-	private $updater;
30
-	/** @var Checker|MockObject */
31
-	private $checker;
32
-	/** @var Installer|MockObject */
33
-	private $installer;
20
+    /** @var ServerVersion|MockObject */
21
+    private $serverVersion;
22
+    /** @var IConfig|MockObject */
23
+    private $config;
24
+    /** @var IAppConfig|MockObject */
25
+    private $appConfig;
26
+    /** @var LoggerInterface|MockObject */
27
+    private $logger;
28
+    /** @var Updater */
29
+    private $updater;
30
+    /** @var Checker|MockObject */
31
+    private $checker;
32
+    /** @var Installer|MockObject */
33
+    private $installer;
34 34
 
35
-	protected function setUp(): void {
36
-		parent::setUp();
37
-		$this->serverVersion = $this->createMock(ServerVersion::class);
38
-		$this->config = $this->createMock(IConfig::class);
39
-		$this->appConfig = $this->createMock(IAppConfig::class);
40
-		$this->logger = $this->createMock(LoggerInterface::class);
41
-		$this->checker = $this->createMock(Checker::class);
42
-		$this->installer = $this->createMock(Installer::class);
35
+    protected function setUp(): void {
36
+        parent::setUp();
37
+        $this->serverVersion = $this->createMock(ServerVersion::class);
38
+        $this->config = $this->createMock(IConfig::class);
39
+        $this->appConfig = $this->createMock(IAppConfig::class);
40
+        $this->logger = $this->createMock(LoggerInterface::class);
41
+        $this->checker = $this->createMock(Checker::class);
42
+        $this->installer = $this->createMock(Installer::class);
43 43
 
44
-		$this->updater = new Updater(
45
-			$this->serverVersion,
46
-			$this->config,
47
-			$this->appConfig,
48
-			$this->checker,
49
-			$this->logger,
50
-			$this->installer
51
-		);
52
-	}
44
+        $this->updater = new Updater(
45
+            $this->serverVersion,
46
+            $this->config,
47
+            $this->appConfig,
48
+            $this->checker,
49
+            $this->logger,
50
+            $this->installer
51
+        );
52
+    }
53 53
 
54
-	/**
55
-	 * @return array
56
-	 */
57
-	public static function versionCompatibilityTestData(): array {
58
-		return [
59
-			// Upgrade with invalid version
60
-			['9.1.1.13', '11.0.2.25', ['nextcloud' => ['11.0' => true]], false],
61
-			['10.0.1.13', '11.0.2.25', ['nextcloud' => ['11.0' => true]], false],
62
-			// Upgrad with valid version
63
-			['11.0.1.13', '11.0.2.25', ['nextcloud' => ['11.0' => true]], true],
64
-			// Downgrade with valid version
65
-			['11.0.2.25', '11.0.1.13', ['nextcloud' => ['11.0' => true]], false],
66
-			['11.0.2.25', '11.0.1.13', ['nextcloud' => ['11.0' => true]], true, true],
67
-			// Downgrade with invalid version
68
-			['11.0.2.25', '10.0.1.13', ['nextcloud' => ['10.0' => true]], false],
69
-			['11.0.2.25', '10.0.1.13', ['nextcloud' => ['10.0' => true]], false, true],
54
+    /**
55
+     * @return array
56
+     */
57
+    public static function versionCompatibilityTestData(): array {
58
+        return [
59
+            // Upgrade with invalid version
60
+            ['9.1.1.13', '11.0.2.25', ['nextcloud' => ['11.0' => true]], false],
61
+            ['10.0.1.13', '11.0.2.25', ['nextcloud' => ['11.0' => true]], false],
62
+            // Upgrad with valid version
63
+            ['11.0.1.13', '11.0.2.25', ['nextcloud' => ['11.0' => true]], true],
64
+            // Downgrade with valid version
65
+            ['11.0.2.25', '11.0.1.13', ['nextcloud' => ['11.0' => true]], false],
66
+            ['11.0.2.25', '11.0.1.13', ['nextcloud' => ['11.0' => true]], true, true],
67
+            // Downgrade with invalid version
68
+            ['11.0.2.25', '10.0.1.13', ['nextcloud' => ['10.0' => true]], false],
69
+            ['11.0.2.25', '10.0.1.13', ['nextcloud' => ['10.0' => true]], false, true],
70 70
 
71
-			// Migration with unknown vendor
72
-			['9.1.1.13', '11.0.2.25', ['nextcloud' => ['9.1' => true]], false, false, 'owncloud'],
73
-			['9.1.1.13', '11.0.2.25', ['nextcloud' => ['9.1' => true]], false, true, 'owncloud'],
74
-			// Migration with unsupported vendor version
75
-			['9.1.1.13', '11.0.2.25', ['owncloud' => ['10.0' => true]], false, false, 'owncloud'],
76
-			['9.1.1.13', '11.0.2.25', ['owncloud' => ['10.0' => true]], false, true, 'owncloud'],
77
-			// Migration with valid vendor version
78
-			['9.1.1.13', '11.0.2.25', ['owncloud' => ['9.1' => true]], true, false, 'owncloud'],
79
-			['9.1.1.13', '11.0.2.25', ['owncloud' => ['9.1' => true]], true, true, 'owncloud'],
80
-		];
81
-	}
71
+            // Migration with unknown vendor
72
+            ['9.1.1.13', '11.0.2.25', ['nextcloud' => ['9.1' => true]], false, false, 'owncloud'],
73
+            ['9.1.1.13', '11.0.2.25', ['nextcloud' => ['9.1' => true]], false, true, 'owncloud'],
74
+            // Migration with unsupported vendor version
75
+            ['9.1.1.13', '11.0.2.25', ['owncloud' => ['10.0' => true]], false, false, 'owncloud'],
76
+            ['9.1.1.13', '11.0.2.25', ['owncloud' => ['10.0' => true]], false, true, 'owncloud'],
77
+            // Migration with valid vendor version
78
+            ['9.1.1.13', '11.0.2.25', ['owncloud' => ['9.1' => true]], true, false, 'owncloud'],
79
+            ['9.1.1.13', '11.0.2.25', ['owncloud' => ['9.1' => true]], true, true, 'owncloud'],
80
+        ];
81
+    }
82 82
 
83
-	/**
84
-	 * @dataProvider versionCompatibilityTestData
85
-	 *
86
-	 * @param string $oldVersion
87
-	 * @param string $newVersion
88
-	 * @param array $allowedVersions
89
-	 * @param bool $result
90
-	 * @param bool $debug
91
-	 * @param string $vendor
92
-	 */
93
-	public function testIsUpgradePossible($oldVersion, $newVersion, $allowedVersions, $result, $debug = false, $vendor = 'nextcloud'): void {
94
-		$this->config->expects($this->any())
95
-			->method('getSystemValueBool')
96
-			->with('debug', false)
97
-			->willReturn($debug);
98
-		$this->config->expects($this->any())
99
-			->method('getAppValue')
100
-			->with('core', 'vendor', '')
101
-			->willReturn($vendor);
83
+    /**
84
+     * @dataProvider versionCompatibilityTestData
85
+     *
86
+     * @param string $oldVersion
87
+     * @param string $newVersion
88
+     * @param array $allowedVersions
89
+     * @param bool $result
90
+     * @param bool $debug
91
+     * @param string $vendor
92
+     */
93
+    public function testIsUpgradePossible($oldVersion, $newVersion, $allowedVersions, $result, $debug = false, $vendor = 'nextcloud'): void {
94
+        $this->config->expects($this->any())
95
+            ->method('getSystemValueBool')
96
+            ->with('debug', false)
97
+            ->willReturn($debug);
98
+        $this->config->expects($this->any())
99
+            ->method('getAppValue')
100
+            ->with('core', 'vendor', '')
101
+            ->willReturn($vendor);
102 102
 
103
-		$this->assertSame($result, $this->updater->isUpgradePossible($oldVersion, $newVersion, $allowedVersions));
104
-	}
103
+        $this->assertSame($result, $this->updater->isUpgradePossible($oldVersion, $newVersion, $allowedVersions));
104
+    }
105 105
 }
Please login to merge, or discard this patch.
tests/lib/Files/Stream/HashWrapperTest.php 1 patch
Indentation   +25 added lines, -25 removed lines patch added patch discarded remove patch
@@ -12,31 +12,31 @@
 block discarded – undo
12 12
 use Test\TestCase;
13 13
 
14 14
 class HashWrapperTest extends TestCase {
15
-	/**
16
-	 * @dataProvider hashProvider
17
-	 */
18
-	public function testHashStream($data, string $algo, string $hash): void {
19
-		if (!is_resource($data)) {
20
-			$tmpData = fopen('php://temp', 'r+');
21
-			if ($data !== null) {
22
-				fwrite($tmpData, $data);
23
-				rewind($tmpData);
24
-			}
25
-			$data = $tmpData;
26
-		}
15
+    /**
16
+     * @dataProvider hashProvider
17
+     */
18
+    public function testHashStream($data, string $algo, string $hash): void {
19
+        if (!is_resource($data)) {
20
+            $tmpData = fopen('php://temp', 'r+');
21
+            if ($data !== null) {
22
+                fwrite($tmpData, $data);
23
+                rewind($tmpData);
24
+            }
25
+            $data = $tmpData;
26
+        }
27 27
 
28
-		$wrapper = HashWrapper::wrap($data, $algo, function ($result) use ($hash) {
29
-			$this->assertEquals($hash, $result);
30
-		});
31
-		stream_get_contents($wrapper);
32
-	}
28
+        $wrapper = HashWrapper::wrap($data, $algo, function ($result) use ($hash) {
29
+            $this->assertEquals($hash, $result);
30
+        });
31
+        stream_get_contents($wrapper);
32
+    }
33 33
 
34
-	public static function hashProvider(): array {
35
-		return [
36
-			['foo', 'md5', 'acbd18db4cc2f85cedef654fccc4a4d8'],
37
-			['foo', 'sha1', '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'],
38
-			['foo', 'sha256', '2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae'],
39
-			[str_repeat('foo', 8192), 'md5', '96684d2b796a2c54a026b5d60f9de819'],
40
-		];
41
-	}
34
+    public static function hashProvider(): array {
35
+        return [
36
+            ['foo', 'md5', 'acbd18db4cc2f85cedef654fccc4a4d8'],
37
+            ['foo', 'sha1', '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'],
38
+            ['foo', 'sha256', '2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae'],
39
+            [str_repeat('foo', 8192), 'md5', '96684d2b796a2c54a026b5d60f9de819'],
40
+        ];
41
+    }
42 42
 }
Please login to merge, or discard this patch.
tests/lib/Files/Stream/EncryptionTest.php 2 patches
Indentation   +362 added lines, -362 removed lines patch added patch discarded remove patch
@@ -26,366 +26,366 @@
 block discarded – undo
26 26
 use Psr\Log\LoggerInterface;
27 27
 
28 28
 class EncryptionTest extends \Test\TestCase {
29
-	public const DEFAULT_WRAPPER = '\OC\Files\Stream\Encryption';
30
-
31
-	private IEncryptionModule&MockObject $encryptionModule;
32
-
33
-	/**
34
-	 * @param class-string<Wrapper> $wrapper
35
-	 * @return resource
36
-	 */
37
-	protected function getStream(string $fileName, string $mode, int $unencryptedSize, string $wrapper = self::DEFAULT_WRAPPER, int $unencryptedSizeOnClose = 0) {
38
-		clearstatcache();
39
-		$size = filesize($fileName);
40
-		$source = fopen($fileName, $mode);
41
-		$internalPath = $fileName;
42
-		$fullPath = $fileName;
43
-		$header = [];
44
-		$uid = '';
45
-		$this->encryptionModule = $this->buildMockModule();
46
-		$cache = $this->createMock(ICache::class);
47
-		$storage = $this->getMockBuilder('\OC\Files\Storage\Storage')
48
-			->disableOriginalConstructor()->getMock();
49
-		$encStorage = $this->getMockBuilder('\OC\Files\Storage\Wrapper\Encryption')
50
-			->disableOriginalConstructor()->getMock();
51
-		$config = $this->getMockBuilder(IConfig::class)
52
-			->disableOriginalConstructor()
53
-			->getMock();
54
-		$arrayCache = $this->createMock(ArrayCache::class);
55
-		$groupManager = $this->getMockBuilder('\OC\Group\Manager')
56
-			->disableOriginalConstructor()
57
-			->getMock();
58
-		$file = $this->getMockBuilder('\OC\Encryption\File')
59
-			->disableOriginalConstructor()
60
-			->onlyMethods(['getAccessList'])
61
-			->getMock();
62
-		$file->expects($this->any())->method('getAccessList')->willReturn([]);
63
-		$util = $this->getMockBuilder('\OC\Encryption\Util')
64
-			->onlyMethods(['getUidAndFilename'])
65
-			->setConstructorArgs([new View(), new \OC\User\Manager(
66
-				$config,
67
-				$this->createMock(ICacheFactory::class),
68
-				$this->createMock(IEventDispatcher::class),
69
-				$this->createMock(LoggerInterface::class),
70
-			), $groupManager, $config, $arrayCache])
71
-			->getMock();
72
-		$util->expects($this->any())
73
-			->method('getUidAndFilename')
74
-			->willReturn(['user1', $internalPath]);
75
-		$storage->expects($this->any())->method('getCache')->willReturn($cache);
76
-		$entry = new CacheEntry([
77
-			'fileid' => 5,
78
-			'encryptedVersion' => 2,
79
-			'unencrypted_size' => $unencryptedSizeOnClose,
80
-		]);
81
-		$cache->expects($this->any())->method('get')->willReturn($entry);
82
-		$cache->expects($this->any())->method('update')->with(5, ['encrypted' => 3, 'encryptedVersion' => 3, 'unencrypted_size' => $unencryptedSizeOnClose]);
83
-
84
-		return $wrapper::wrap(
85
-			$source,
86
-			$internalPath,
87
-			$fullPath,
88
-			$header,
89
-			$uid,
90
-			$this->encryptionModule,
91
-			$storage,
92
-			$encStorage,
93
-			$util,
94
-			$file,
95
-			$mode,
96
-			$size,
97
-			$unencryptedSize,
98
-			8192,
99
-			true,
100
-			$wrapper,
101
-		);
102
-	}
103
-
104
-	/**
105
-	 * @dataProvider dataProviderStreamOpen()
106
-	 */
107
-	public function testStreamOpen(
108
-		$isMasterKeyUsed,
109
-		$mode,
110
-		$fullPath,
111
-		$fileExists,
112
-		$expectedSharePath,
113
-		$expectedSize,
114
-		$expectedUnencryptedSize,
115
-		$expectedReadOnly,
116
-	): void {
117
-		// build mocks
118
-		$encryptionModuleMock = $this->createMock(IEncryptionModule::class);
119
-		$encryptionModuleMock->expects($this->any())->method('needDetailedAccessList')->willReturn(!$isMasterKeyUsed);
120
-		$encryptionModuleMock->expects($this->once())
121
-			->method('getUnencryptedBlockSize')->willReturn(99);
122
-		$encryptionModuleMock->expects($this->once())
123
-			->method('begin')->willReturn([]);
124
-
125
-		$storageMock = $this->createMock(Storage::class);
126
-		$storageMock->expects($this->once())->method('file_exists')->willReturn($fileExists);
127
-
128
-		$fileMock = $this->createMock(File::class);
129
-		if ($isMasterKeyUsed) {
130
-			$fileMock->expects($this->never())->method('getAccessList');
131
-		} else {
132
-			$fileMock->expects($this->once())->method('getAccessList')
133
-				->willReturnCallback(function ($sharePath) use ($expectedSharePath) {
134
-					$this->assertSame($expectedSharePath, $sharePath);
135
-					return [];
136
-				});
137
-		}
138
-		$utilMock = $this->getMockBuilder(Util::class)
139
-			->disableOriginalConstructor()->getMock();
140
-		$utilMock->expects($this->any())
141
-			->method('getHeaderSize')
142
-			->willReturn(8192);
143
-
144
-		// get a instance of the stream wrapper
145
-		$streamWrapper = $this->getMockBuilder(Encryption::class)
146
-			->onlyMethods(['loadContext', 'writeHeader', 'skipHeader'])
147
-			->disableOriginalConstructor()
148
-			->getMock();
149
-
150
-		// set internal properties of the stream wrapper
151
-		$stream = new \ReflectionClass(Encryption::class);
152
-		$encryptionModule = $stream->getProperty('encryptionModule');
153
-		$encryptionModule->setAccessible(true);
154
-		$encryptionModule->setValue($streamWrapper, $encryptionModuleMock);
155
-		$encryptionModule->setAccessible(false);
156
-		$storage = $stream->getProperty('storage');
157
-		$storage->setAccessible(true);
158
-		$storage->setValue($streamWrapper, $storageMock);
159
-		$storage->setAccessible(false);
160
-		$file = $stream->getProperty('file');
161
-		$file->setAccessible(true);
162
-		$file->setValue($streamWrapper, $fileMock);
163
-		$file->setAccessible(false);
164
-		$util = $stream->getProperty('util');
165
-		$util->setAccessible(true);
166
-		$util->setValue($streamWrapper, $utilMock);
167
-		$util->setAccessible(false);
168
-		$fullPathP = $stream->getProperty('fullPath');
169
-		$fullPathP->setAccessible(true);
170
-		$fullPathP->setValue($streamWrapper, $fullPath);
171
-		$fullPathP->setAccessible(false);
172
-		$header = $stream->getProperty('header');
173
-		$header->setAccessible(true);
174
-		$header->setValue($streamWrapper, []);
175
-		$header->setAccessible(false);
176
-		$this->invokePrivate($streamWrapper, 'signed', [true]);
177
-		$this->invokePrivate($streamWrapper, 'internalPath', [$fullPath]);
178
-		$this->invokePrivate($streamWrapper, 'uid', ['test']);
179
-
180
-		// call stream_open, that's the method we want to test
181
-		$dummyVar = 'foo';
182
-		$streamWrapper->stream_open('', $mode, '', $dummyVar);
183
-
184
-		// check internal properties
185
-		$size = $stream->getProperty('size');
186
-		$size->setAccessible(true);
187
-		$this->assertSame($expectedSize, $size->getValue($streamWrapper));
188
-		$size->setAccessible(false);
189
-
190
-		$unencryptedSize = $stream->getProperty('unencryptedSize');
191
-		$unencryptedSize->setAccessible(true);
192
-		$this->assertSame($expectedUnencryptedSize, $unencryptedSize->getValue($streamWrapper));
193
-		$unencryptedSize->setAccessible(false);
194
-
195
-		$readOnly = $stream->getProperty('readOnly');
196
-		$readOnly->setAccessible(true);
197
-		$this->assertSame($expectedReadOnly, $readOnly->getValue($streamWrapper));
198
-		$readOnly->setAccessible(false);
199
-	}
200
-
201
-	public static function dataProviderStreamOpen(): array {
202
-		return [
203
-			[false, 'r', '/foo/bar/test.txt', true, '/foo/bar/test.txt', null, null, true],
204
-			[false, 'r', '/foo/bar/test.txt', false, '/foo/bar', null, null, true],
205
-			[false, 'w', '/foo/bar/test.txt', true, '/foo/bar/test.txt', 8192, 0, false],
206
-			[true, 'r', '/foo/bar/test.txt', true, '/foo/bar/test.txt', null, null, true],
207
-			[true, 'r', '/foo/bar/test.txt', false, '/foo/bar', null, null, true],
208
-			[true, 'w', '/foo/bar/test.txt', true, '/foo/bar/test.txt', 8192, 0, false],
209
-		];
210
-	}
211
-
212
-	public function testWriteRead(): void {
213
-		$fileName = tempnam('/tmp', 'FOO');
214
-		$stream = $this->getStream($fileName, 'w+', 0, self::DEFAULT_WRAPPER, 6);
215
-		$this->assertEquals(6, fwrite($stream, 'foobar'));
216
-		fclose($stream);
217
-
218
-		$stream = $this->getStream($fileName, 'r', 6);
219
-		$this->assertEquals('foobar', fread($stream, 100));
220
-		fclose($stream);
221
-
222
-		$stream = $this->getStream($fileName, 'r+', 6, self::DEFAULT_WRAPPER, 6);
223
-		$this->assertEquals(3, fwrite($stream, 'bar'));
224
-		fclose($stream);
225
-
226
-		$stream = $this->getStream($fileName, 'r', 6);
227
-		$this->assertEquals('barbar', fread($stream, 100));
228
-		fclose($stream);
229
-
230
-		unlink($fileName);
231
-	}
232
-
233
-	public function testRewind(): void {
234
-		$fileName = tempnam('/tmp', 'FOO');
235
-		$stream = $this->getStream($fileName, 'w+', 0, self::DEFAULT_WRAPPER, 6);
236
-		$this->assertEquals(6, fwrite($stream, 'foobar'));
237
-		$this->assertEquals(true, rewind($stream));
238
-		$this->assertEquals('foobar', fread($stream, 100));
239
-		$this->assertEquals(true, rewind($stream));
240
-		$this->assertEquals(3, fwrite($stream, 'bar'));
241
-		fclose($stream);
242
-
243
-		$stream = $this->getStream($fileName, 'r', 6);
244
-		$this->assertEquals('barbar', fread($stream, 100));
245
-		fclose($stream);
246
-
247
-		unlink($fileName);
248
-	}
249
-
250
-	public function testSeek(): void {
251
-		$fileName = tempnam('/tmp', 'FOO');
252
-
253
-		$stream = $this->getStream($fileName, 'w+', 0, self::DEFAULT_WRAPPER, 9);
254
-		$this->assertEquals(6, fwrite($stream, 'foobar'));
255
-		$this->assertEquals(0, fseek($stream, 3));
256
-		$this->assertEquals(6, fwrite($stream, 'foobar'));
257
-		fclose($stream);
258
-
259
-		$stream = $this->getStream($fileName, 'r', 9);
260
-		$this->assertEquals('foofoobar', fread($stream, 100));
261
-		$this->assertEquals(-1, fseek($stream, 10));
262
-		$this->assertEquals(0, fseek($stream, 9));
263
-		$this->assertEquals(-1, fseek($stream, -10, SEEK_CUR));
264
-		$this->assertEquals(0, fseek($stream, -9, SEEK_CUR));
265
-		$this->assertEquals(-1, fseek($stream, -10, SEEK_END));
266
-		$this->assertEquals(0, fseek($stream, -9, SEEK_END));
267
-		fclose($stream);
268
-
269
-		unlink($fileName);
270
-	}
271
-
272
-	public static function dataFilesProvider(): array {
273
-		return [
274
-			['lorem-big.txt'],
275
-			['block-aligned.txt'],
276
-			['block-aligned-plus-one.txt'],
277
-		];
278
-	}
279
-
280
-	/**
281
-	 * @dataProvider dataFilesProvider
282
-	 */
283
-	public function testWriteReadBigFile($testFile): void {
284
-		$expectedData = file_get_contents(\OC::$SERVERROOT . '/tests/data/' . $testFile);
285
-		// write it
286
-		$fileName = tempnam('/tmp', 'FOO');
287
-		$stream = $this->getStream($fileName, 'w+', 0, self::DEFAULT_WRAPPER, strlen($expectedData));
288
-		// while writing the file from the beginning to the end we should never try
289
-		// to read parts of the file. This should only happen for write operations
290
-		// in the middle of a file
291
-		$this->encryptionModule->expects($this->never())->method('decrypt');
292
-		fwrite($stream, $expectedData);
293
-		fclose($stream);
294
-
295
-		// read it all
296
-		$stream = $this->getStream($fileName, 'r', strlen($expectedData));
297
-		$data = stream_get_contents($stream);
298
-		fclose($stream);
299
-
300
-		$this->assertEquals($expectedData, $data);
301
-
302
-		// another read test with a loop like we do in several places:
303
-		$stream = $this->getStream($fileName, 'r', strlen($expectedData));
304
-		$data = '';
305
-		while (!feof($stream)) {
306
-			$data .= fread($stream, 8192);
307
-		}
308
-		fclose($stream);
309
-
310
-		$this->assertEquals($expectedData, $data);
311
-
312
-		unlink($fileName);
313
-	}
314
-
315
-	/**
316
-	 * simulate a non-seekable storage
317
-	 *
318
-	 * @dataProvider dataFilesProvider
319
-	 */
320
-	public function testWriteToNonSeekableStorage($testFile): void {
321
-		$wrapper = $this->getMockBuilder(Encryption::class)
322
-			->onlyMethods(['parentStreamSeek'])
323
-			->getMock();
324
-		$wrapper->expects($this->any())
325
-			->method('parentStreamSeek')
326
-			->willReturn(false);
327
-
328
-		$expectedData = file_get_contents(\OC::$SERVERROOT . '/tests/data/' . $testFile);
329
-		// write it
330
-		$fileName = tempnam('/tmp', 'FOO');
331
-		$stream = $this->getStream($fileName, 'w+', 0, '\Test\Files\Stream\DummyEncryptionWrapper', strlen($expectedData));
332
-		// while writing the file from the beginning to the end we should never try
333
-		// to read parts of the file. This should only happen for write operations
334
-		// in the middle of a file
335
-		$this->encryptionModule->expects($this->never())->method('decrypt');
336
-		fwrite($stream, $expectedData);
337
-		fclose($stream);
338
-
339
-		// read it all
340
-		$stream = $this->getStream($fileName, 'r', strlen($expectedData), '\Test\Files\Stream\DummyEncryptionWrapper', strlen($expectedData));
341
-		$data = stream_get_contents($stream);
342
-		fclose($stream);
343
-
344
-		$this->assertEquals($expectedData, $data);
345
-
346
-		// another read test with a loop like we do in several places:
347
-		$stream = $this->getStream($fileName, 'r', strlen($expectedData));
348
-		$data = '';
349
-		while (!feof($stream)) {
350
-			$data .= fread($stream, 8192);
351
-		}
352
-		fclose($stream);
353
-
354
-		$this->assertEquals($expectedData, $data);
355
-
356
-		unlink($fileName);
357
-	}
358
-
359
-	protected function buildMockModule(): IEncryptionModule&MockObject {
360
-		$encryptionModule = $this->getMockBuilder(IEncryptionModule::class)
361
-			->disableOriginalConstructor()
362
-			->onlyMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable', 'encryptAll', 'prepareDecryptAll', 'isReadyForUser', 'needDetailedAccessList'])
363
-			->getMock();
364
-
365
-		$encryptionModule->expects($this->any())->method('getId')->willReturn('UNIT_TEST_MODULE');
366
-		$encryptionModule->expects($this->any())->method('getDisplayName')->willReturn('Unit test module');
367
-		$encryptionModule->expects($this->any())->method('begin')->willReturn([]);
368
-		$encryptionModule->expects($this->any())->method('end')->willReturn('');
369
-		$encryptionModule->expects($this->any())->method('isReadable')->willReturn(true);
370
-		$encryptionModule->expects($this->any())->method('needDetailedAccessList')->willReturn(false);
371
-		$encryptionModule->expects($this->any())->method('encrypt')->willReturnCallback(function ($data) {
372
-			// simulate different block size by adding some padding to the data
373
-			if (isset($data[6125])) {
374
-				return str_pad($data, 8192, 'X');
375
-			}
376
-			// last block
377
-			return $data;
378
-		});
379
-		$encryptionModule->expects($this->any())->method('decrypt')->willReturnCallback(function ($data) {
380
-			if (isset($data[8191])) {
381
-				return substr($data, 0, 6126);
382
-			}
383
-			// last block
384
-			return $data;
385
-		});
386
-		$encryptionModule->expects($this->any())->method('update')->willReturn(true);
387
-		$encryptionModule->expects($this->any())->method('shouldEncrypt')->willReturn(true);
388
-		$encryptionModule->expects($this->any())->method('getUnencryptedBlockSize')->willReturn(6126);
389
-		return $encryptionModule;
390
-	}
29
+    public const DEFAULT_WRAPPER = '\OC\Files\Stream\Encryption';
30
+
31
+    private IEncryptionModule&MockObject $encryptionModule;
32
+
33
+    /**
34
+     * @param class-string<Wrapper> $wrapper
35
+     * @return resource
36
+     */
37
+    protected function getStream(string $fileName, string $mode, int $unencryptedSize, string $wrapper = self::DEFAULT_WRAPPER, int $unencryptedSizeOnClose = 0) {
38
+        clearstatcache();
39
+        $size = filesize($fileName);
40
+        $source = fopen($fileName, $mode);
41
+        $internalPath = $fileName;
42
+        $fullPath = $fileName;
43
+        $header = [];
44
+        $uid = '';
45
+        $this->encryptionModule = $this->buildMockModule();
46
+        $cache = $this->createMock(ICache::class);
47
+        $storage = $this->getMockBuilder('\OC\Files\Storage\Storage')
48
+            ->disableOriginalConstructor()->getMock();
49
+        $encStorage = $this->getMockBuilder('\OC\Files\Storage\Wrapper\Encryption')
50
+            ->disableOriginalConstructor()->getMock();
51
+        $config = $this->getMockBuilder(IConfig::class)
52
+            ->disableOriginalConstructor()
53
+            ->getMock();
54
+        $arrayCache = $this->createMock(ArrayCache::class);
55
+        $groupManager = $this->getMockBuilder('\OC\Group\Manager')
56
+            ->disableOriginalConstructor()
57
+            ->getMock();
58
+        $file = $this->getMockBuilder('\OC\Encryption\File')
59
+            ->disableOriginalConstructor()
60
+            ->onlyMethods(['getAccessList'])
61
+            ->getMock();
62
+        $file->expects($this->any())->method('getAccessList')->willReturn([]);
63
+        $util = $this->getMockBuilder('\OC\Encryption\Util')
64
+            ->onlyMethods(['getUidAndFilename'])
65
+            ->setConstructorArgs([new View(), new \OC\User\Manager(
66
+                $config,
67
+                $this->createMock(ICacheFactory::class),
68
+                $this->createMock(IEventDispatcher::class),
69
+                $this->createMock(LoggerInterface::class),
70
+            ), $groupManager, $config, $arrayCache])
71
+            ->getMock();
72
+        $util->expects($this->any())
73
+            ->method('getUidAndFilename')
74
+            ->willReturn(['user1', $internalPath]);
75
+        $storage->expects($this->any())->method('getCache')->willReturn($cache);
76
+        $entry = new CacheEntry([
77
+            'fileid' => 5,
78
+            'encryptedVersion' => 2,
79
+            'unencrypted_size' => $unencryptedSizeOnClose,
80
+        ]);
81
+        $cache->expects($this->any())->method('get')->willReturn($entry);
82
+        $cache->expects($this->any())->method('update')->with(5, ['encrypted' => 3, 'encryptedVersion' => 3, 'unencrypted_size' => $unencryptedSizeOnClose]);
83
+
84
+        return $wrapper::wrap(
85
+            $source,
86
+            $internalPath,
87
+            $fullPath,
88
+            $header,
89
+            $uid,
90
+            $this->encryptionModule,
91
+            $storage,
92
+            $encStorage,
93
+            $util,
94
+            $file,
95
+            $mode,
96
+            $size,
97
+            $unencryptedSize,
98
+            8192,
99
+            true,
100
+            $wrapper,
101
+        );
102
+    }
103
+
104
+    /**
105
+     * @dataProvider dataProviderStreamOpen()
106
+     */
107
+    public function testStreamOpen(
108
+        $isMasterKeyUsed,
109
+        $mode,
110
+        $fullPath,
111
+        $fileExists,
112
+        $expectedSharePath,
113
+        $expectedSize,
114
+        $expectedUnencryptedSize,
115
+        $expectedReadOnly,
116
+    ): void {
117
+        // build mocks
118
+        $encryptionModuleMock = $this->createMock(IEncryptionModule::class);
119
+        $encryptionModuleMock->expects($this->any())->method('needDetailedAccessList')->willReturn(!$isMasterKeyUsed);
120
+        $encryptionModuleMock->expects($this->once())
121
+            ->method('getUnencryptedBlockSize')->willReturn(99);
122
+        $encryptionModuleMock->expects($this->once())
123
+            ->method('begin')->willReturn([]);
124
+
125
+        $storageMock = $this->createMock(Storage::class);
126
+        $storageMock->expects($this->once())->method('file_exists')->willReturn($fileExists);
127
+
128
+        $fileMock = $this->createMock(File::class);
129
+        if ($isMasterKeyUsed) {
130
+            $fileMock->expects($this->never())->method('getAccessList');
131
+        } else {
132
+            $fileMock->expects($this->once())->method('getAccessList')
133
+                ->willReturnCallback(function ($sharePath) use ($expectedSharePath) {
134
+                    $this->assertSame($expectedSharePath, $sharePath);
135
+                    return [];
136
+                });
137
+        }
138
+        $utilMock = $this->getMockBuilder(Util::class)
139
+            ->disableOriginalConstructor()->getMock();
140
+        $utilMock->expects($this->any())
141
+            ->method('getHeaderSize')
142
+            ->willReturn(8192);
143
+
144
+        // get a instance of the stream wrapper
145
+        $streamWrapper = $this->getMockBuilder(Encryption::class)
146
+            ->onlyMethods(['loadContext', 'writeHeader', 'skipHeader'])
147
+            ->disableOriginalConstructor()
148
+            ->getMock();
149
+
150
+        // set internal properties of the stream wrapper
151
+        $stream = new \ReflectionClass(Encryption::class);
152
+        $encryptionModule = $stream->getProperty('encryptionModule');
153
+        $encryptionModule->setAccessible(true);
154
+        $encryptionModule->setValue($streamWrapper, $encryptionModuleMock);
155
+        $encryptionModule->setAccessible(false);
156
+        $storage = $stream->getProperty('storage');
157
+        $storage->setAccessible(true);
158
+        $storage->setValue($streamWrapper, $storageMock);
159
+        $storage->setAccessible(false);
160
+        $file = $stream->getProperty('file');
161
+        $file->setAccessible(true);
162
+        $file->setValue($streamWrapper, $fileMock);
163
+        $file->setAccessible(false);
164
+        $util = $stream->getProperty('util');
165
+        $util->setAccessible(true);
166
+        $util->setValue($streamWrapper, $utilMock);
167
+        $util->setAccessible(false);
168
+        $fullPathP = $stream->getProperty('fullPath');
169
+        $fullPathP->setAccessible(true);
170
+        $fullPathP->setValue($streamWrapper, $fullPath);
171
+        $fullPathP->setAccessible(false);
172
+        $header = $stream->getProperty('header');
173
+        $header->setAccessible(true);
174
+        $header->setValue($streamWrapper, []);
175
+        $header->setAccessible(false);
176
+        $this->invokePrivate($streamWrapper, 'signed', [true]);
177
+        $this->invokePrivate($streamWrapper, 'internalPath', [$fullPath]);
178
+        $this->invokePrivate($streamWrapper, 'uid', ['test']);
179
+
180
+        // call stream_open, that's the method we want to test
181
+        $dummyVar = 'foo';
182
+        $streamWrapper->stream_open('', $mode, '', $dummyVar);
183
+
184
+        // check internal properties
185
+        $size = $stream->getProperty('size');
186
+        $size->setAccessible(true);
187
+        $this->assertSame($expectedSize, $size->getValue($streamWrapper));
188
+        $size->setAccessible(false);
189
+
190
+        $unencryptedSize = $stream->getProperty('unencryptedSize');
191
+        $unencryptedSize->setAccessible(true);
192
+        $this->assertSame($expectedUnencryptedSize, $unencryptedSize->getValue($streamWrapper));
193
+        $unencryptedSize->setAccessible(false);
194
+
195
+        $readOnly = $stream->getProperty('readOnly');
196
+        $readOnly->setAccessible(true);
197
+        $this->assertSame($expectedReadOnly, $readOnly->getValue($streamWrapper));
198
+        $readOnly->setAccessible(false);
199
+    }
200
+
201
+    public static function dataProviderStreamOpen(): array {
202
+        return [
203
+            [false, 'r', '/foo/bar/test.txt', true, '/foo/bar/test.txt', null, null, true],
204
+            [false, 'r', '/foo/bar/test.txt', false, '/foo/bar', null, null, true],
205
+            [false, 'w', '/foo/bar/test.txt', true, '/foo/bar/test.txt', 8192, 0, false],
206
+            [true, 'r', '/foo/bar/test.txt', true, '/foo/bar/test.txt', null, null, true],
207
+            [true, 'r', '/foo/bar/test.txt', false, '/foo/bar', null, null, true],
208
+            [true, 'w', '/foo/bar/test.txt', true, '/foo/bar/test.txt', 8192, 0, false],
209
+        ];
210
+    }
211
+
212
+    public function testWriteRead(): void {
213
+        $fileName = tempnam('/tmp', 'FOO');
214
+        $stream = $this->getStream($fileName, 'w+', 0, self::DEFAULT_WRAPPER, 6);
215
+        $this->assertEquals(6, fwrite($stream, 'foobar'));
216
+        fclose($stream);
217
+
218
+        $stream = $this->getStream($fileName, 'r', 6);
219
+        $this->assertEquals('foobar', fread($stream, 100));
220
+        fclose($stream);
221
+
222
+        $stream = $this->getStream($fileName, 'r+', 6, self::DEFAULT_WRAPPER, 6);
223
+        $this->assertEquals(3, fwrite($stream, 'bar'));
224
+        fclose($stream);
225
+
226
+        $stream = $this->getStream($fileName, 'r', 6);
227
+        $this->assertEquals('barbar', fread($stream, 100));
228
+        fclose($stream);
229
+
230
+        unlink($fileName);
231
+    }
232
+
233
+    public function testRewind(): void {
234
+        $fileName = tempnam('/tmp', 'FOO');
235
+        $stream = $this->getStream($fileName, 'w+', 0, self::DEFAULT_WRAPPER, 6);
236
+        $this->assertEquals(6, fwrite($stream, 'foobar'));
237
+        $this->assertEquals(true, rewind($stream));
238
+        $this->assertEquals('foobar', fread($stream, 100));
239
+        $this->assertEquals(true, rewind($stream));
240
+        $this->assertEquals(3, fwrite($stream, 'bar'));
241
+        fclose($stream);
242
+
243
+        $stream = $this->getStream($fileName, 'r', 6);
244
+        $this->assertEquals('barbar', fread($stream, 100));
245
+        fclose($stream);
246
+
247
+        unlink($fileName);
248
+    }
249
+
250
+    public function testSeek(): void {
251
+        $fileName = tempnam('/tmp', 'FOO');
252
+
253
+        $stream = $this->getStream($fileName, 'w+', 0, self::DEFAULT_WRAPPER, 9);
254
+        $this->assertEquals(6, fwrite($stream, 'foobar'));
255
+        $this->assertEquals(0, fseek($stream, 3));
256
+        $this->assertEquals(6, fwrite($stream, 'foobar'));
257
+        fclose($stream);
258
+
259
+        $stream = $this->getStream($fileName, 'r', 9);
260
+        $this->assertEquals('foofoobar', fread($stream, 100));
261
+        $this->assertEquals(-1, fseek($stream, 10));
262
+        $this->assertEquals(0, fseek($stream, 9));
263
+        $this->assertEquals(-1, fseek($stream, -10, SEEK_CUR));
264
+        $this->assertEquals(0, fseek($stream, -9, SEEK_CUR));
265
+        $this->assertEquals(-1, fseek($stream, -10, SEEK_END));
266
+        $this->assertEquals(0, fseek($stream, -9, SEEK_END));
267
+        fclose($stream);
268
+
269
+        unlink($fileName);
270
+    }
271
+
272
+    public static function dataFilesProvider(): array {
273
+        return [
274
+            ['lorem-big.txt'],
275
+            ['block-aligned.txt'],
276
+            ['block-aligned-plus-one.txt'],
277
+        ];
278
+    }
279
+
280
+    /**
281
+     * @dataProvider dataFilesProvider
282
+     */
283
+    public function testWriteReadBigFile($testFile): void {
284
+        $expectedData = file_get_contents(\OC::$SERVERROOT . '/tests/data/' . $testFile);
285
+        // write it
286
+        $fileName = tempnam('/tmp', 'FOO');
287
+        $stream = $this->getStream($fileName, 'w+', 0, self::DEFAULT_WRAPPER, strlen($expectedData));
288
+        // while writing the file from the beginning to the end we should never try
289
+        // to read parts of the file. This should only happen for write operations
290
+        // in the middle of a file
291
+        $this->encryptionModule->expects($this->never())->method('decrypt');
292
+        fwrite($stream, $expectedData);
293
+        fclose($stream);
294
+
295
+        // read it all
296
+        $stream = $this->getStream($fileName, 'r', strlen($expectedData));
297
+        $data = stream_get_contents($stream);
298
+        fclose($stream);
299
+
300
+        $this->assertEquals($expectedData, $data);
301
+
302
+        // another read test with a loop like we do in several places:
303
+        $stream = $this->getStream($fileName, 'r', strlen($expectedData));
304
+        $data = '';
305
+        while (!feof($stream)) {
306
+            $data .= fread($stream, 8192);
307
+        }
308
+        fclose($stream);
309
+
310
+        $this->assertEquals($expectedData, $data);
311
+
312
+        unlink($fileName);
313
+    }
314
+
315
+    /**
316
+     * simulate a non-seekable storage
317
+     *
318
+     * @dataProvider dataFilesProvider
319
+     */
320
+    public function testWriteToNonSeekableStorage($testFile): void {
321
+        $wrapper = $this->getMockBuilder(Encryption::class)
322
+            ->onlyMethods(['parentStreamSeek'])
323
+            ->getMock();
324
+        $wrapper->expects($this->any())
325
+            ->method('parentStreamSeek')
326
+            ->willReturn(false);
327
+
328
+        $expectedData = file_get_contents(\OC::$SERVERROOT . '/tests/data/' . $testFile);
329
+        // write it
330
+        $fileName = tempnam('/tmp', 'FOO');
331
+        $stream = $this->getStream($fileName, 'w+', 0, '\Test\Files\Stream\DummyEncryptionWrapper', strlen($expectedData));
332
+        // while writing the file from the beginning to the end we should never try
333
+        // to read parts of the file. This should only happen for write operations
334
+        // in the middle of a file
335
+        $this->encryptionModule->expects($this->never())->method('decrypt');
336
+        fwrite($stream, $expectedData);
337
+        fclose($stream);
338
+
339
+        // read it all
340
+        $stream = $this->getStream($fileName, 'r', strlen($expectedData), '\Test\Files\Stream\DummyEncryptionWrapper', strlen($expectedData));
341
+        $data = stream_get_contents($stream);
342
+        fclose($stream);
343
+
344
+        $this->assertEquals($expectedData, $data);
345
+
346
+        // another read test with a loop like we do in several places:
347
+        $stream = $this->getStream($fileName, 'r', strlen($expectedData));
348
+        $data = '';
349
+        while (!feof($stream)) {
350
+            $data .= fread($stream, 8192);
351
+        }
352
+        fclose($stream);
353
+
354
+        $this->assertEquals($expectedData, $data);
355
+
356
+        unlink($fileName);
357
+    }
358
+
359
+    protected function buildMockModule(): IEncryptionModule&MockObject {
360
+        $encryptionModule = $this->getMockBuilder(IEncryptionModule::class)
361
+            ->disableOriginalConstructor()
362
+            ->onlyMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable', 'encryptAll', 'prepareDecryptAll', 'isReadyForUser', 'needDetailedAccessList'])
363
+            ->getMock();
364
+
365
+        $encryptionModule->expects($this->any())->method('getId')->willReturn('UNIT_TEST_MODULE');
366
+        $encryptionModule->expects($this->any())->method('getDisplayName')->willReturn('Unit test module');
367
+        $encryptionModule->expects($this->any())->method('begin')->willReturn([]);
368
+        $encryptionModule->expects($this->any())->method('end')->willReturn('');
369
+        $encryptionModule->expects($this->any())->method('isReadable')->willReturn(true);
370
+        $encryptionModule->expects($this->any())->method('needDetailedAccessList')->willReturn(false);
371
+        $encryptionModule->expects($this->any())->method('encrypt')->willReturnCallback(function ($data) {
372
+            // simulate different block size by adding some padding to the data
373
+            if (isset($data[6125])) {
374
+                return str_pad($data, 8192, 'X');
375
+            }
376
+            // last block
377
+            return $data;
378
+        });
379
+        $encryptionModule->expects($this->any())->method('decrypt')->willReturnCallback(function ($data) {
380
+            if (isset($data[8191])) {
381
+                return substr($data, 0, 6126);
382
+            }
383
+            // last block
384
+            return $data;
385
+        });
386
+        $encryptionModule->expects($this->any())->method('update')->willReturn(true);
387
+        $encryptionModule->expects($this->any())->method('shouldEncrypt')->willReturn(true);
388
+        $encryptionModule->expects($this->any())->method('getUnencryptedBlockSize')->willReturn(6126);
389
+        return $encryptionModule;
390
+    }
391 391
 }
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -130,7 +130,7 @@  discard block
 block discarded – undo
130 130
 			$fileMock->expects($this->never())->method('getAccessList');
131 131
 		} else {
132 132
 			$fileMock->expects($this->once())->method('getAccessList')
133
-				->willReturnCallback(function ($sharePath) use ($expectedSharePath) {
133
+				->willReturnCallback(function($sharePath) use ($expectedSharePath) {
134 134
 					$this->assertSame($expectedSharePath, $sharePath);
135 135
 					return [];
136 136
 				});
@@ -281,7 +281,7 @@  discard block
 block discarded – undo
281 281
 	 * @dataProvider dataFilesProvider
282 282
 	 */
283 283
 	public function testWriteReadBigFile($testFile): void {
284
-		$expectedData = file_get_contents(\OC::$SERVERROOT . '/tests/data/' . $testFile);
284
+		$expectedData = file_get_contents(\OC::$SERVERROOT.'/tests/data/'.$testFile);
285 285
 		// write it
286 286
 		$fileName = tempnam('/tmp', 'FOO');
287 287
 		$stream = $this->getStream($fileName, 'w+', 0, self::DEFAULT_WRAPPER, strlen($expectedData));
@@ -325,7 +325,7 @@  discard block
 block discarded – undo
325 325
 			->method('parentStreamSeek')
326 326
 			->willReturn(false);
327 327
 
328
-		$expectedData = file_get_contents(\OC::$SERVERROOT . '/tests/data/' . $testFile);
328
+		$expectedData = file_get_contents(\OC::$SERVERROOT.'/tests/data/'.$testFile);
329 329
 		// write it
330 330
 		$fileName = tempnam('/tmp', 'FOO');
331 331
 		$stream = $this->getStream($fileName, 'w+', 0, '\Test\Files\Stream\DummyEncryptionWrapper', strlen($expectedData));
@@ -368,7 +368,7 @@  discard block
 block discarded – undo
368 368
 		$encryptionModule->expects($this->any())->method('end')->willReturn('');
369 369
 		$encryptionModule->expects($this->any())->method('isReadable')->willReturn(true);
370 370
 		$encryptionModule->expects($this->any())->method('needDetailedAccessList')->willReturn(false);
371
-		$encryptionModule->expects($this->any())->method('encrypt')->willReturnCallback(function ($data) {
371
+		$encryptionModule->expects($this->any())->method('encrypt')->willReturnCallback(function($data) {
372 372
 			// simulate different block size by adding some padding to the data
373 373
 			if (isset($data[6125])) {
374 374
 				return str_pad($data, 8192, 'X');
@@ -376,7 +376,7 @@  discard block
 block discarded – undo
376 376
 			// last block
377 377
 			return $data;
378 378
 		});
379
-		$encryptionModule->expects($this->any())->method('decrypt')->willReturnCallback(function ($data) {
379
+		$encryptionModule->expects($this->any())->method('decrypt')->willReturnCallback(function($data) {
380 380
 			if (isset($data[8191])) {
381 381
 				return substr($data, 0, 6126);
382 382
 			}
Please login to merge, or discard this patch.
tests/lib/Files/PathVerificationTest.php 1 patch
Indentation   +112 added lines, -112 removed lines patch added patch discarded remove patch
@@ -19,116 +19,116 @@
 block discarded – undo
19 19
  * @package Test\Files
20 20
  */
21 21
 class PathVerificationTest extends \Test\TestCase {
22
-	/**
23
-	 * @var \OC\Files\View
24
-	 */
25
-	private $view;
26
-
27
-	protected function setUp(): void {
28
-		parent::setUp();
29
-		$this->view = new View();
30
-	}
31
-
32
-
33
-	public function testPathVerificationFileNameTooLong(): void {
34
-		$this->expectException(\OCP\Files\InvalidPathException::class);
35
-		$this->expectExceptionMessage('Filename is too long');
36
-
37
-		$fileName = str_repeat('a', 500);
38
-		$this->view->verifyPath('', $fileName);
39
-	}
40
-
41
-
42
-	/**
43
-	 * @dataProvider providesEmptyFiles
44
-	 */
45
-	public function testPathVerificationEmptyFileName($fileName): void {
46
-		$this->expectException(\OCP\Files\InvalidPathException::class);
47
-		$this->expectExceptionMessage('Empty filename is not allowed');
48
-
49
-		$this->view->verifyPath('', $fileName);
50
-	}
51
-
52
-	public static function providesEmptyFiles(): array {
53
-		return [
54
-			[''],
55
-			[' '],
56
-		];
57
-	}
58
-
59
-	/**
60
-	 * @dataProvider providesDotFiles
61
-	 */
62
-	public function testPathVerificationDotFiles($fileName): void {
63
-		$this->expectException(\OCP\Files\InvalidPathException::class);
64
-		$this->expectExceptionMessage('Dot files are not allowed');
65
-
66
-		$this->view->verifyPath('', $fileName);
67
-	}
68
-
69
-	public static function providesDotFiles(): array {
70
-		return [
71
-			['.'],
72
-			['..'],
73
-			[' .'],
74
-			[' ..'],
75
-			['. '],
76
-			['.. '],
77
-			[' . '],
78
-			[' .. '],
79
-		];
80
-	}
81
-
82
-	/**
83
-	 * @dataProvider providesAstralPlane
84
-	 */
85
-	public function testPathVerificationAstralPlane($fileName): void {
86
-		$connection = \OC::$server->getDatabaseConnection();
87
-
88
-		if (!$connection->supports4ByteText()) {
89
-			$this->expectException(InvalidPathException::class);
90
-			$this->expectExceptionMessage('File name contains at least one invalid character');
91
-		} else {
92
-			$this->addToAssertionCount(1);
93
-		}
94
-
95
-		$this->view->verifyPath('', $fileName);
96
-	}
97
-
98
-	public static function providesAstralPlane(): array {
99
-		return [
100
-			// this is the monkey emoji - http://en.wikipedia.org/w/index.php?title=%F0%9F%90%B5&redirect=no
101
-			['
Please login to merge, or discard this patch.
tests/lib/Files/Cache/ScannerTest.php 1 patch
Indentation   +422 added lines, -422 removed lines patch added patch discarded remove patch
@@ -24,427 +24,427 @@
 block discarded – undo
24 24
  * @package Test\Files\Cache
25 25
  */
26 26
 class ScannerTest extends TestCase {
27
-	private Storage $storage;
28
-	private Scanner $scanner;
29
-	private Cache $cache;
30
-
31
-	protected function setUp(): void {
32
-		parent::setUp();
33
-
34
-		$this->storage = new Temporary([]);
35
-		$this->scanner = new Scanner($this->storage);
36
-		$this->cache = new Cache($this->storage);
37
-	}
38
-
39
-	protected function tearDown(): void {
40
-		$this->cache->clear();
41
-
42
-		parent::tearDown();
43
-	}
44
-
45
-	public function testFile(): void {
46
-		$data = "dummy file data\n";
47
-		$this->storage->file_put_contents('foo.txt', $data);
48
-		$this->scanner->scanFile('foo.txt');
49
-
50
-		$this->assertEquals($this->cache->inCache('foo.txt'), true);
51
-		$cachedData = $this->cache->get('foo.txt');
52
-		$this->assertEquals($cachedData['size'], strlen($data));
53
-		$this->assertEquals($cachedData['mimetype'], 'text/plain');
54
-		$this->assertNotEquals($cachedData['parent'], -1); //parent folders should be scanned automatically
55
-
56
-		$data = file_get_contents(OC::$SERVERROOT . '/core/img/logo/logo.png');
57
-		$this->storage->file_put_contents('foo.png', $data);
58
-		$this->scanner->scanFile('foo.png');
59
-
60
-		$this->assertEquals($this->cache->inCache('foo.png'), true);
61
-		$cachedData = $this->cache->get('foo.png');
62
-		$this->assertEquals($cachedData['size'], strlen($data));
63
-		$this->assertEquals($cachedData['mimetype'], 'image/png');
64
-	}
65
-
66
-	public function testFile4Byte(): void {
67
-		$data = "dummy file data\n";
68
-		$this->storage->file_put_contents('foo
Please login to merge, or discard this patch.