Completed
Push — master ( 43030a...db557c )
by Maxence
06:50 queued 04:30
created

Test::testUpdatingDocumentsAccess()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 17
rs 9.7
c 0
b 0
f 0
cc 1
nc 1
nop 3
1
<?php
2
/**
3
 * FullTextSearch - Full text search framework for Nextcloud
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the COPYING file.
7
 *
8
 * @author Maxence Lange <[email protected]>
9
 * @copyright 2018
10
 * @license GNU AGPL version 3 or any later version
11
 *
12
 * This program is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License as
14
 * published by the Free Software Foundation, either version 3 of the
15
 * License, or (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License
23
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
24
 *
25
 */
26
27
namespace OCA\FullTextSearch\Command;
28
29
use Exception;
30
use OCA\FullTextSearch\Exceptions\InterruptException;
31
use OCA\FullTextSearch\Exceptions\ProviderDoesNotExistException;
32
use OCA\FullTextSearch\Exceptions\ProviderIsNotCompatibleException;
33
use OCA\FullTextSearch\Exceptions\ProviderIsNotUniqueException;
34
use OCA\FullTextSearch\Exceptions\RunnerAlreadyUpException;
35
use OCA\FullTextSearch\Exceptions\TickDoesNotExistException;
36
use OCA\FullTextSearch\IFullTextSearchPlatform;
37
use OCA\FullTextSearch\IFullTextSearchProvider;
38
use OCA\FullTextSearch\Model\DocumentAccess;
39
use OCA\FullTextSearch\Model\ExtendedBase;
40
use OCA\FullTextSearch\Model\IndexOptions;
41
use OCA\FullTextSearch\Model\Runner;
42
use OCA\FullTextSearch\Model\SearchRequest;
43
use OCA\FullTextSearch\Model\SearchResult;
44
use OCA\FullTextSearch\Provider\TestProvider;
45
use OCA\FullTextSearch\Service\IndexService;
46
use OCA\FullTextSearch\Service\MiscService;
47
use OCA\FullTextSearch\Service\PlatformService;
48
use OCA\FullTextSearch\Service\ProviderService;
49
use OCA\FullTextSearch\Service\RunningService;
50
use OCA\FullTextSearch\Service\TestService;
51
use OCP\AppFramework\QueryException;
52
use Symfony\Component\Console\Input\InputInterface;
53
use Symfony\Component\Console\Input\InputOption;
54
use Symfony\Component\Console\Output\OutputInterface;
55
56
57
class Test extends ExtendedBase {
58
59
	const DELAY_STABILIZE_PLATFORM = 3;
60
61
	/** @var RunningService */
62
	private $runningService;
63
64
	/** @var PlatformService */
65
	private $platformService;
66
67
	/** @var ProviderService */
68
	private $providerService;
69
70
	/** @var IndexService */
71
	private $indexService;
72
73
	/** @var TestService */
74
	private $testService;
75
76
	/** @var MiscService */
77
	private $miscService;
78
79
80
	/** @var Runner */
81
	private $runner;
82
83
84
	/** @var boolean */
85
	private $isJson = false;
86
87
	/**
88
	 * Index constructor.
89
	 *
90
	 * @param RunningService $runningService
91
	 * @param ProviderService $providerService
92
	 * @param IndexService $indexService
93
	 * @param PlatformService $platformService
94
	 * @param TestService $testService
95
	 * @param MiscService $miscService
96
	 */
97 View Code Duplication
	public function __construct(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
98
		RunningService $runningService, PlatformService $platformService,
99
		ProviderService $providerService, IndexService $indexService, TestService $testService,
100
		MiscService $miscService
101
	) {
102
		parent::__construct();
103
104
		$this->runningService = $runningService;
105
		$this->platformService = $platformService;
106
		$this->providerService = $providerService;
107
		$this->indexService = $indexService;
108
		$this->testService = $testService;
109
		$this->miscService = $miscService;
110
	}
111
112
113
	/**
114
	 *
115
	 */
116
	protected function configure() {
117
		parent::configure();
118
		$this->setName('fulltextsearch:test')
119
			 ->setDescription('Testing the platform setup')
120
			 ->addOption('json', 'j', InputOption::VALUE_NONE, 'return result as JSON')
121
			 ->addOption(
122
				 'platform_delay', 'd', InputOption::VALUE_REQUIRED,
123
				 'change DELAY_STABILIZE_PLATFORM'
124
			 );
125
	}
126
127
128
	/**
129
	 * @param InputInterface $input
130
	 * @param OutputInterface $output
131
	 *
132
	 * @return int|null|void
133
	 * @throws Exception
134
	 */
135
	protected function execute(InputInterface $input, OutputInterface $output) {
136
		$this->isJson = ($input->getOption('json') === true);
137
		$platformDelay = ($input->getOption('platform_delay') > 0) ? $input->getOption(
138
			'platform_delay'
139
		) : self::DELAY_STABILIZE_PLATFORM;
140
141
		echo '$' . $input->getOption('platform_delay') . '$';
142
		$this->output($output, '.Testing your current setup:');
143
144
		try {
145
			$testProvider = $this->testCreatingProvider($output);
146
			$this->testMockedProvider($output, $testProvider);
147
			$testPlatform = $this->testLoadingPlatform($output);
148
			$this->testLockingProcess($output, $testPlatform, $testProvider);
149
		} catch (Exception $e) {
150
			$this->output($output, false);
151
			throw $e;
152
		}
153
154
		try {
155
			$this->testResetTest($output, $testProvider);
156
			$this->pause($output, $platformDelay);
157
			$this->testIndexingDocuments($output, $testPlatform, $testProvider);
158
			$this->pause($output, $platformDelay);
159
			$this->testContentLicense($output, $testPlatform);
160
			$this->testSearchSimple($output, $testPlatform, $testProvider);
161
162
			$this->testUpdatingDocumentsAccess($output, $testPlatform, $testProvider);
163
			$this->pause($output, $platformDelay);
164
			$this->testSearchAccess($output, $testPlatform, $testProvider);
165
			$this->testSearchShare($output, $testPlatform, $testProvider);
166
167
			$this->testResetTest($output, $testProvider);
168
			$this->testUnlockingProcess($output);
169
		} catch (Exception $e) {
170
			$this->output($output, false);
171
			$this->output($output, 'Error detected, unlocking process');
172
			$this->runner->stop();
173
			$this->output($output, true);
174
175
			throw $e;
176
		}
177
178
		$this->output($output, '', true);
179
	}
180
181
182
	/**
183
	 * @return IFullTextSearchProvider
184
	 * @throws ProviderIsNotCompatibleException
185
	 * @throws QueryException
186
	 * @throws ProviderDoesNotExistException
187
	 * @throws ProviderIsNotUniqueException
188
	 */
189
	private function generateMockProvider() {
190
		$this->providerService->loadProvider('OCA\FullTextSearch\Provider\TestProvider');
191
192
		return $this->providerService->getProvider(TestProvider::TEST_PROVIDER_ID);
193
	}
194
195
196
	/**
197
	 * @param OutputInterface $output
198
	 * @param string|bool $line
199
	 * @param bool $isNewLine
200
	 */
201
	private function output(OutputInterface $output, $line, $isNewLine = true) {
202
		$line = $this->convertBoolToLine($line, $isNewLine);
203
		if ($isNewLine) {
204
			$output->write(' ', true);
205
		}
206
207
		$output->write($line . ' ', false);
208
	}
209
210
211
	/**
212
	 * @param string|bool $line
213
	 * @param $isNewLine
214
	 *
215
	 * @return string
216
	 */
217
	private function convertBoolToLine($line, &$isNewLine) {
218
		if (!is_bool($line)) {
219
			return $line;
220
		}
221
222
		$isNewLine = false;
223
		if ($line === false) {
224
			return '<error>fail</error>';
225
		}
226
227
		return '<info>ok</info>';
228
	}
229
230
231
	/**
232
	 * @param $output
233
	 *
234
	 * @return IFullTextSearchProvider
235
	 * @throws ProviderDoesNotExistException
236
	 * @throws ProviderIsNotCompatibleException
237
	 * @throws ProviderIsNotUniqueException
238
	 * @throws QueryException
239
	 */
240
	private function testCreatingProvider($output) {
241
		$this->output($output, 'Creating mocked content provider.');
242
		$testProvider = $this->generateMockProvider();
243
		$this->output($output, true);
244
245
		return $testProvider;
246
	}
247
248
249
	/**
250
	 * @param $output
251
	 * @param IFullTextSearchProvider $testProvider
252
	 */
253
	private function testMockedProvider($output, IFullTextSearchProvider $testProvider) {
254
		$this->output($output, 'Testing mocked provider: get indexable documents.');
255
		$indexableDocuments =
256
			$testProvider->generateIndexableDocuments(TestService::DOCUMENT_USER1);
257
		$this->output($output, '(' . sizeof($indexableDocuments) . ' items)', false);
258
		$this->output($output, true);
259
	}
260
261
262
	/**
263
	 * @param $output
264
	 *
265
	 * @return IFullTextSearchPlatform
266
	 * @throws Exception
267
	 */
268
	private function testLoadingPlatform($output) {
269
		$this->output($output, 'Loading search platform.');
270
		$testPlatform = $this->platformService->getPlatform();
271
		$this->output($output, '(' . $testPlatform->getName() . ')', false);
272
		$this->output($output, true);
273
274
		$this->output($output, 'Testing search platform.');
275
		$this->output(
276
			$output, (($testPlatform->testPlatform()) ? 'found index' : 'index not found'), false
277
		);
278
		$this->output($output, true);
279
280
		return $testPlatform;
281
	}
282
283
284
	/**
285
	 * @param OutputInterface $output
286
	 * @param IFullTextSearchPlatform $testPlatform
287
	 * @param IFullTextSearchProvider $testProvider
288
	 *
289
	 * @throws RunnerAlreadyUpException
290
	 */
291
	private function testLockingProcess(
292
		OutputInterface $output, IFullTextSearchPlatform $testPlatform,
293
		IFullTextSearchProvider $testProvider
294
	) {
295
		$this->output($output, 'Locking process');
296
		$this->runner = new Runner($this->runningService, 'test');
297
		$this->runner->start(true);
298
		$this->indexService->setRunner($this->runner);
299
		$testPlatform->setRunner($this->runner);
300
		$testProvider->setRunner($this->runner);
301
		$this->output($output, true);
302
	}
303
304
305
	/**
306
	 * @param OutputInterface $output
307
	 * @param IFullTextSearchProvider $testProvider
308
	 *
309
	 * @throws Exception
310
	 */
311
	private function testResetTest(OutputInterface $output, IFullTextSearchProvider $testProvider
312
	) {
313
		$this->output($output, 'Removing test.');
314
		$this->indexService->resetIndex($testProvider->getId());
315
		$this->output($output, true);
316
	}
317
318
319
	/**
320
	 * @param OutputInterface $output
321
	 * @param IFullTextSearchPlatform $testPlatform
322
	 * @param IFullTextSearchProvider $testProvider
323
	 *
324
	 * @throws InterruptException
325
	 * @throws TickDoesNotExistException
326
	 */
327
	private function testIndexingDocuments(
328
		OutputInterface $output, IFullTextSearchPlatform $testPlatform,
329
		IFullTextSearchProvider $testProvider
330
	) {
331
		$this->output($output, 'Indexing generated documents.');
332
		$options = new IndexOptions(
333
			[
334
				'provider' => TestProvider::TEST_PROVIDER_ID
335
			]
336
		);
337
		$this->indexService->indexProviderContentFromUser(
338
			$testPlatform, $testProvider, TestService::DOCUMENT_USER1, $options
339
		);
340
		$this->output($output, true);
341
	}
342
343
344
	/**
345
	 * @param OutputInterface $output
346
	 * @param IFullTextSearchPlatform $testPlatform
347
	 *
348
	 * @throws Exception
349
	 */
350
	private function testContentLicense(
351
		OutputInterface $output, IFullTextSearchPlatform $testPlatform
352
	) {
353
354
		try {
355
			$this->output($output, 'Retreiving content from a big index (license).');
356
			$indexDocument = $testPlatform->getDocument(
0 ignored issues
show
Bug introduced by
The method getDocument() does not seem to exist on object<OCA\FullTextSearc...FullTextSearchPlatform>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
357
				TestProvider::TEST_PROVIDER_ID, TestService::DOCUMENT_TYPE_LICENSE
358
			);
359
360
			$this->output(
361
				$output, '(size: ' . $indexDocument->getContentSize() . ')', false
362
			);
363
			$this->output($output, true);
364
		} catch (Exception $e) {
365
			throw new Exception(
366
				"Issue while getting test document '" . TestService::DOCUMENT_TYPE_LICENSE
367
				. "' from search platform: " . $e->getMessage()
368
			);
369
		}
370
371
		$this->output($output, 'Comparing document with source.');
372
		$this->testService->compareIndexDocument(
373
			$this->testService->generateIndexDocumentContentLicense(), $indexDocument
374
		);
375
		$this->output($output, true);
376
	}
377
378
379
	/**
380
	 * @param OutputInterface $output
381
	 * @param IFullTextSearchPlatform $testPlatform
382
	 *
383
	 * @param IFullTextSearchProvider $testProvider
384
	 *
385
	 * @throws Exception
386
	 */
387
	private function testSearchSimple(
388
		OutputInterface $output, IFullTextSearchPlatform $testPlatform,
389
		IFullTextSearchProvider $testProvider
390
	) {
391
392
		$this->output($output, 'Searching basic keywords:');
393
394
		$access = new DocumentAccess();
395
		$access->setViewerId(TestService::DOCUMENT_USER1);
396
397
		$this->search(
398
			$output, $testPlatform, $testProvider, $access, 'test',
399
			[TestService::DOCUMENT_TYPE_SIMPLE]
400
		);
401
		$this->search(
402
			$output, $testPlatform, $testProvider, $access, 'this is test',
403
			[TestService::DOCUMENT_TYPE_SIMPLE, TestService::DOCUMENT_TYPE_LICENSE]
404
		);
405
		$this->search(
406
			$output, $testPlatform, $testProvider, $access, '"this is test"',
407
			[]
408
		);
409
		$this->search(
410
			$output, $testPlatform, $testProvider, $access, '"this is a test"',
411
			[TestService::DOCUMENT_TYPE_SIMPLE]
412
		);
413
		$this->search(
414
			$output, $testPlatform, $testProvider, $access, 'this is -test',
415
			[TestService::DOCUMENT_TYPE_LICENSE]
416
		);
417
		$this->search(
418
			$output, $testPlatform, $testProvider, $access, 'this is +test',
419
			[TestService::DOCUMENT_TYPE_SIMPLE]
420
		);
421
		$this->search(
422
			$output, $testPlatform, $testProvider, $access, '-this is test',
423
			[]
424
		);
425
	}
426
427
428
	/**
429
	 * @param OutputInterface $output
430
	 * @param IFullTextSearchPlatform $testPlatform
431
	 * @param IFullTextSearchProvider $testProvider
432
	 *
433
	 * @throws InterruptException
434
	 * @throws TickDoesNotExistException
435
	 */
436
	private function testUpdatingDocumentsAccess(
437
		OutputInterface $output, IFullTextSearchPlatform $testPlatform,
438
		IFullTextSearchProvider $testProvider
439
	) {
440
		$this->output($output, 'Updating documents access.');
441
		$options = new IndexOptions(
442
			[
443
				'provider'                            => TestProvider::TEST_PROVIDER_ID,
444
				TestService::DOCUMENT_INDEXING_OPTION => TestService::DOCUMENT_INDEXING_ACCESS
445
			]
446
		);
447
		$testProvider->setIndexOptions($options);
448
		$this->indexService->indexProviderContentFromUser(
449
			$testPlatform, $testProvider, TestService::DOCUMENT_USER1, $options
450
		);
451
		$this->output($output, true);
452
	}
453
454
455
	/**
456
	 * @param OutputInterface $output
457
	 * @param IFullTextSearchPlatform $platform
458
	 *
459
	 * @param IFullTextSearchProvider $provider
460
	 *
461
	 * @throws Exception
462
	 */
463
	private function testSearchAccess(
464
		OutputInterface $output, IFullTextSearchPlatform $platform,
465
		IFullTextSearchProvider $provider
466
	) {
467
468
		$this->output($output, 'Searching with group access rights:');
469
470
		$this->searchGroups($output, $platform, $provider, [], []);
471
		$this->searchGroups(
472
			$output, $platform, $provider, [TestService::DOCUMENT_GROUP1],
473
			[TestService::DOCUMENT_TYPE_LICENSE]
474
		);
475
		$this->searchGroups(
476
			$output, $platform, $provider,
477
			[TestService::DOCUMENT_GROUP1, TestService::DOCUMENT_GROUP2],
478
			[TestService::DOCUMENT_TYPE_LICENSE]
479
		);
480
		$this->searchGroups(
481
			$output, $platform, $provider,
482
			[TestService::DOCUMENT_NOTGROUP, TestService::DOCUMENT_GROUP2],
483
			[TestService::DOCUMENT_TYPE_LICENSE]
484
		);
485
		$this->searchGroups($output, $platform, $provider, [TestService::DOCUMENT_NOTGROUP], []);
486
	}
487
488
489
	/**
490
	 * @param OutputInterface $output
491
	 * @param IFullTextSearchPlatform $platform
492
	 *
493
	 * @param IFullTextSearchProvider $provider
494
	 *
495
	 * @throws Exception
496
	 */
497
	private function testSearchShare(
498
		OutputInterface $output, IFullTextSearchPlatform $platform,
499
		IFullTextSearchProvider $provider
500
	) {
501
502
		$this->output($output, 'Searching with share rights:');
503
504
		$this->searchUsers($output, $platform, $provider, TestService::DOCUMENT_NOTUSER, []);
505
		$this->searchUsers($output, $platform, $provider, TestService::DOCUMENT_USER2, ['license']);
506
		$this->searchUsers($output, $platform, $provider, TestService::DOCUMENT_USER3, ['license']);
507
	}
508
509
510
	/**
511
	 * @param OutputInterface $output
512
	 *
513
	 * @throws TickDoesNotExistException
514
	 */
515
	private function testUnlockingProcess(OutputInterface $output) {
516
		$this->output($output, 'Unlocking process');
517
		$this->runner->stop();
518
		$this->output($output, true);
519
	}
520
521
522
	/**
523
	 * @param OutputInterface $output
524
	 * @param IFullTextSearchPlatform $testPlatform
525
	 * @param IFullTextSearchProvider $testProvider
526
	 * @param DocumentAccess $access
527
	 * @param string $search
528
	 * @param array $expected
529
	 * @param string $moreOutput
530
	 *
531
	 * @throws Exception
532
	 */
533
	private function search(
534
		OutputInterface $output, IFullTextSearchPlatform $testPlatform,
535
		IFullTextSearchProvider $testProvider,
536
		DocumentAccess $access, $search, $expected, $moreOutput = ''
537
	) {
538
		$this->output(
539
			$output,
540
			" - '" . $search . "'" . (($moreOutput === '') ? '' : ' - ' . $moreOutput . ' - ')
541
		);
542
		$request = new SearchRequest();
543
544
		$request->setSearch($search);
545
		$searchResult = $testPlatform->searchDocuments($testProvider, $access, $request);
546
		$this->output(
547
			$output,
548
			'(result: ' . $searchResult->getCount() . ', expected: ' . json_encode($expected) . ')',
549
			false
550
		);
551
		$this->compareSearchResult($searchResult, $expected);
552
		$this->output($output, true);
553
	}
554
555
556
	/**
557
	 * @param OutputInterface $output
558
	 * @param IFullTextSearchPlatform $testPlatform
559
	 * @param IFullTextSearchProvider $testProvider
560
	 * @param array $groups
561
	 * @param array $expected
562
	 *
563
	 * @throws Exception
564
	 */
565 View Code Duplication
	private function searchGroups(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
566
		OutputInterface $output, IFullTextSearchPlatform $testPlatform,
567
		IFullTextSearchProvider $testProvider, $groups, $expected
568
	) {
569
570
		$access = new DocumentAccess();
571
		$access->setViewerId(TestService::DOCUMENT_NOTUSER);
572
		$access->setGroups($groups);
573
574
		$this->search(
575
			$output, $testPlatform, $testProvider, $access, 'license',
576
			$expected, json_encode($groups)
577
		);
578
	}
579
580
581
	/**
582
	 * @param OutputInterface $output
583
	 * @param IFullTextSearchPlatform $testPlatform
584
	 * @param IFullTextSearchProvider $testProvider
585
	 * @param string $user
586
	 * @param array $expected
587
	 *
588
	 * @throws Exception
589
	 */
590 View Code Duplication
	private function searchUsers(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
591
		OutputInterface $output, IFullTextSearchPlatform $testPlatform,
592
		IFullTextSearchProvider $testProvider, $user, $expected
593
	) {
594
		$access = new DocumentAccess();
595
		$access->setViewerId($user);
596
		$this->search(
597
			$output, $testPlatform, $testProvider, $access, 'license',
598
			$expected, $user
599
		);
600
	}
601
602
603
	/**
604
	 * @param SearchResult $searchResult
605
	 * @param $entries
606
	 *
607
	 * @throws Exception
608
	 */
609
	private function compareSearchResult(SearchResult $searchResult, $entries) {
610
		$documents = $searchResult->getDocuments();
611
		if (sizeof($documents) !== sizeof($entries)) {
612
			throw new \Exception('Unexpected SearchResult: ' . json_encode($searchResult));
613
		}
614
615
		foreach ($documents as $document) {
616
			if (!in_array($document->getId(), $entries)) {
617
				throw new \Exception('Unexpected Document: ' . json_encode($document));
618
			}
619
		}
620
	}
621
622
623
	/**
624
	 * @param OutputInterface $output
625
	 * @param int $s
626
	 *
627
	 * @throws InterruptException
628
	 */
629
	private function pause(OutputInterface $output, $s) {
630
		$this->output($output, 'Pausing ' . $s . ' seconds');
631
632
		for ($i = 1; $i <= $s; $i++) {
633
			if (time_nanosleep(1, 0) !== true) {
634
				throw new InterruptException('Interrupted by user');
635
			}
636
637
			$this->output($output, $i, false);
638
		}
639
640
		$this->output($output, true);
641
	}
642
643
}
644
645
646
647